Find the lexicographically smallest string by prepending characters
Last Updated :
08 Mar, 2024
Two Players A and B are playing a game using a non-empty string S of length N having lowercase English Alphabets. The length of string S is even. Each player also has a string of their own, initially empty. In one move, a player takes either the first or the last character of the string S, removes it from S and prepends it (adds to the beginning) to their own string. The game ends when the string S becomes empty. The winner is the player with the lexicographically smaller string. If the players’ string are equal, then it’s a draw. The task is to determine the winner of the game or if the game ends in a draw.
Note: Player A starts the game.
Examples:
Input: S = “geek”
Output: A
Explanation: Player A can always manage to win:
- Player A chooses ‘g’, then Player B chooses ‘e’, then Player A chooses ‘e’ and Player B chooses ‘k’. Final String of Player A = “eg” and final string of Player B = “ke”.
- Player A chooses ‘g’ then Player B chooses ‘k’, then Player A chooses ‘e’ and Player B chooses ‘e’. Final String of Player A = “eg” and final string of Player B = “ek”.
In both the cases, Player A can always have a lexicographically smaller string, therefore Player A is the winner.
Input: S = “gffg”
Output: Draw
Explanation: It can be seen that the game will always end in a draw if both the player play optimally.
Approach: To solve the problem, follow the below idea:
The problem can be solved using Dynamic Programming. Maintain a 2D array dp[][], such that dp[l][r] stores the winner for substring S[l:r]. Declare a recursive function, say solve(l, r) which returns integers corresponding to the outcomes:
- If the outcome is a draw, then return 0.
- If the player A wins, then return 1.
- If the player B wins, then return 2.
At any state(l, r), Player A will choose one character, say c and then Player B will choose another character, say d. Then, they both prepended the characters to their string and continued the game with the smaller string S. Now, if we want to calculate the outcome of state (l, r) from (l’, r’), then we can say that we append the letters c and d.
Let’s say the new state becomes (l’, r’). So, if the outcome of (l’, r’) is not a Draw, then the outcome will remain unchanged for state(l, r). Otherwise, if the outcome of state (l’, r’) is a draw, then the outcome depends on the characters c and d.
Explore all the cases, where the player A can start from the beginning as well as from the end. If at any end, Player A can guarantee the win then Player A can win, otherwise the game might end in a Draw or Player B winning the game.
The final answer will be at dp[0][N-1].
Step-by-step algorithm:
- Maintain a 2D array dp[][], such that dp[l][r] stores the winner for substring S[l…r].
- For a substring S[l…r], explore all the four cases by choosing different letters from different sides:
- Player A chooses from the front and Player B chooses from the front, dp[l + 2, r].
- Player A chooses from the front and Player B chooses from the back, dp[l + 1, r – 1].
- Player A chooses from the back and Player B chooses from the front, dp[l + 1, r – 1].
- Player A chooses from the back and Player B chooses from the back, dp[l, r – 2].
- Check for the current characters chosen along with the solutions of the subproblems to find the winner of the game.
- The answer will be stored at dp[0][N-1].
Below is the implementation of the algorithm:
C++
#include <bits/stdc++.h>
using namespace std;
int solve(string s, int l, int r, vector<vector< int > >& dp)
{
if (r - l == 1) {
if (s[l] == s[r])
return 0;
return 1;
}
if (dp[l][r] != -1) {
return dp[l][r];
}
int res1 = solve(s, l + 1, r - 1, dp);
if (res1 == 0) {
if (s[l] < s[r])
res1 = 1;
else if (s[l] > s[r])
res1 = 2;
}
int res2 = solve(s, l + 2, r, dp);
if (res2 == 0) {
if (s[l] < s[l + 1])
res2 = 1;
else if (s[l] > s[l + 1])
res2 = 2;
}
int res3 = solve(s, l + 1, r - 1, dp);
if (res3 == 0) {
if (s[l] > s[r])
res3 = 1;
else if (s[l] < s[r])
res3 = 2;
}
int res4 = solve(s, l, r - 2, dp);
if (res4 == 0) {
if (s[r - 1] > s[r])
res4 = 1;
else if (s[r - 1] < s[r])
res4 = 2;
}
if ((res1 == 1 && res2 == 1)
|| (res3 == 1 && res4 == 1)) {
return dp[l][r] = 1;
}
if (res1 == 2 && res2 == 2 && res3 == 2 && res4 == 2) {
return dp[l][r] = 2;
}
return dp[l][r] = 0;
}
int main()
{
string s = "geek" ;
int n = s.length();
vector<vector< int > > dp(n, vector< int >(n, -1));
int res = solve(s, 0, n - 1, dp);
if (res == 0)
cout << "Draw\n" ;
else if (res == 1)
cout << "A\n" ;
else
cout << "B\n" ;
return 0;
}
|
Java
import java.util.Arrays;
public class Main {
static int solve(String s, int l, int r, int [][] dp) {
if (r - l == 1 ) {
if (s.charAt(l) == s.charAt(r))
return 0 ;
return 1 ;
}
if (dp[l][r] != - 1 ) {
return dp[l][r];
}
int res1 = solve(s, l + 1 , r - 1 , dp);
if (res1 == 0 ) {
if (s.charAt(l) < s.charAt(r))
res1 = 1 ;
else if (s.charAt(l) > s.charAt(r))
res1 = 2 ;
}
int res2 = solve(s, l + 2 , r, dp);
if (res2 == 0 ) {
if (s.charAt(l) < s.charAt(l + 1 ))
res2 = 1 ;
else if (s.charAt(l) > s.charAt(l + 1 ))
res2 = 2 ;
}
int res3 = solve(s, l + 1 , r - 1 , dp);
if (res3 == 0 ) {
if (s.charAt(l) > s.charAt(r))
res3 = 1 ;
else if (s.charAt(l) < s.charAt(r))
res3 = 2 ;
}
int res4 = solve(s, l, r - 2 , dp);
if (res4 == 0 ) {
if (s.charAt(r - 1 ) > s.charAt(r))
res4 = 1 ;
else if (s.charAt(r - 1 ) < s.charAt(r))
res4 = 2 ;
}
if ((res1 == 1 && res2 == 1 ) || (res3 == 1 && res4 == 1 )) {
return dp[l][r] = 1 ;
}
if (res1 == 2 && res2 == 2 && res3 == 2 && res4 == 2 ) {
return dp[l][r] = 2 ;
}
return dp[l][r] = 0 ;
}
public static void main(String[] args) {
String s = "geek" ;
int n = s.length();
int [][] dp = new int [n][n];
for ( int [] row : dp) {
Arrays.fill(row, - 1 );
}
int res = solve(s, 0 , n - 1 , dp);
if (res == 0 )
System.out.println( "Draw" );
else if (res == 1 )
System.out.println( "A" );
else
System.out.println( "B" );
}
}
|
C#
using System;
using System.Collections.Generic;
class Program {
static int Solve( string s, int l, int r,
List<List< int > > dp)
{
if (r - l == 1) {
if (s[l] == s[r])
return 0;
return 1;
}
if (dp[l][r] != -1) {
return dp[l][r];
}
int res1 = Solve(s, l + 1, r - 1, dp);
if (res1 == 0) {
if (s[l] < s[r])
res1 = 1;
else if (s[l] > s[r])
res1 = 2;
}
int res2 = Solve(s, l + 2, r, dp);
if (res2 == 0) {
if (s[l] < s[l + 1])
res2 = 1;
else if (s[l] > s[l + 1])
res2 = 2;
}
int res3 = Solve(s, l + 1, r - 1, dp);
if (res3 == 0) {
if (s[l] > s[r])
res3 = 1;
else if (s[l] < s[r])
res3 = 2;
}
int res4 = Solve(s, l, r - 2, dp);
if (res4 == 0) {
if (s[r - 1] > s[r])
res4 = 1;
else if (s[r - 1] < s[r])
res4 = 2;
}
if ((res1 == 1 && res2 == 1)
|| (res3 == 1 && res4 == 1)) {
return dp[l][r] = 1;
}
if (res1 == 2 && res2 == 2 && res3 == 2
&& res4 == 2) {
return dp[l][r] = 2;
}
return dp[l][r] = 0;
}
static void Main( string [] args)
{
string s = "geek" ;
int n = s.Length;
List<List< int > > dp = new List<List< int > >();
for ( int i = 0; i < n; i++) {
dp.Add( new List< int >());
for ( int j = 0; j < n; j++) {
dp[i].Add(-1);
}
}
int res = Solve(s, 0, n - 1, dp);
if (res == 0)
Console.WriteLine( "Draw" );
else if (res == 1)
Console.WriteLine( "A" );
else
Console.WriteLine( "B" );
}
}
|
Javascript
function solve(s, l, r, dp) {
if (r - l === 1) {
if (s[l] === s[r])
return 0;
return 1;
}
if (dp[l][r] !== -1) {
return dp[l][r];
}
let res1 = solve(s, l + 1, r - 1, dp);
if (res1 === 0) {
if (s[l] < s[r])
res1 = 1;
else if (s[l] > s[r])
res1 = 2;
}
let res2 = solve(s, l + 2, r, dp);
if (res2 === 0) {
if (s[l] < s[l + 1])
res2 = 1;
else if (s[l] > s[l + 1])
res2 = 2;
}
let res3 = solve(s, l + 1, r - 1, dp);
if (res3 === 0) {
if (s[l] > s[r])
res3 = 1;
else if (s[l] < s[r])
res3 = 2;
}
let res4 = solve(s, l, r - 2, dp);
if (res4 === 0) {
if (s[r - 1] > s[r])
res4 = 1;
else if (s[r - 1] < s[r])
res4 = 2;
}
if ((res1 === 1 && res2 === 1)
|| (res3 === 1 && res4 === 1)) {
return dp[l][r] = 1;
}
if (res1 === 2 && res2 === 2 && res3 === 2 && res4 === 2) {
return dp[l][r] = 2;
}
return dp[l][r] = 0;
}
function main() {
let s = "geek" ;
let n = s.length;
let dp = new Array(n).fill( null ).map(() => new Array(n).fill(-1));
let res = solve(s, 0, n - 1, dp);
if (res === 0)
console.log( "Draw" );
else if (res === 1)
console.log( "A" );
else
console.log( "B" );
}
main();
|
Python3
def solve(s, l, r, dp):
if r - l = = 1 :
if s[l] = = s[r]:
return 0
return 1
if dp[l][r] ! = - 1 :
return dp[l][r]
res1 = solve(s, l + 1 , r - 1 , dp)
if res1 = = 0 :
if s[l] < s[r]:
res1 = 1
elif s[l] > s[r]:
res1 = 2
res2 = solve(s, l + 2 , r, dp)
if res2 = = 0 :
if s[l] < s[l + 1 ]:
res2 = 1
elif s[l] > s[l + 1 ]:
res2 = 2
res3 = solve(s, l + 1 , r - 1 , dp)
if res3 = = 0 :
if s[l] > s[r]:
res3 = 1
elif s[l] < s[r]:
res3 = 2
res4 = solve(s, l, r - 2 , dp)
if res4 = = 0 :
if s[r - 1 ] > s[r]:
res4 = 1
elif s[r - 1 ] < s[r]:
res4 = 2
if (res1 = = 1 and res2 = = 1 ) or (res3 = = 1 and res4 = = 1 ):
dp[l][r] = 1
return 1
if res1 = = 2 and res2 = = 2 and res3 = = 2 and res4 = = 2 :
dp[l][r] = 2
return 2
dp[l][r] = 0
return 0
if __name__ = = "__main__" :
s = "geek"
n = len (s)
dp = [[ - 1 for _ in range (n)] for _ in range (n)]
res = solve(s, 0 , n - 1 , dp)
if res = = 0 :
print ( "Draw" )
elif res = = 1 :
print ( "A" )
else :
print ( "B" )
|
Time Complexity: O(N * N), where N is the length of the input string S.
Auxiliary Space: O(N * N)
Share your thoughts in the comments
Please Login to comment...