Given a string s and an integer k, determine whether s can be converted into a palindrome using exactly k insertions. In a single insertion, any character may be inserted at any position in the string.
Return true if such a transformation is possible, otherwise return false.
Examples:
Input: s = "abac", k = 2
Output: true
Explanation: We can insert 'c' at the beginning and 'b' between the two 'a' characters to obtain "cabbac", which is a palindrome.
Input: s = "abcde", k = 3
Output: false
Explanation: It is not possible to obtain a palindrome using exactly 3 insertions.
Table of Content
[Naive Approach] Using Recursion - O(2 ^ n) Time and O(n) Space
The idea is to recursively find the minimum insertions needed for every substring. If the characters at both ends are the same, move inward. Otherwise, try inserting a matching character at either end and take the minimum of the two possibilities. If the minimum insertions required are at most
k, then exactlykinsertions are also possible by performing any remaining insertions without breaking the palindrome.
#include <string>
#include <iostream>
using namespace std;
int minInsertions(string &s, int i, int j)
{
// Base case: Empty string or single character.
if (i >= j)
return 0;
// If both end characters are same,
// no insertion is needed.
if (s[i] == s[j])
return minInsertions(s, i + 1, j - 1);
// Otherwise, insert at either end
// and choose the better option.
return 1 + min(minInsertions(s, i + 1, j), minInsertions(s, i, j - 1));
}
bool isPossible(string &s, int k)
{
// Find the minimum insertions required.
int req = minInsertions(s, 0, s.size() - 1);
// Check whether exactly k insertions are possible.
return req <= k;
}
int main()
{
string s = "abac";
int k = 2;
cout << (isPossible(s, k)? "true" : "false");
return 0;
}
public class GFG {
int minInsertions(String s, int i, int j)
{
// Base case: Empty string or single character.
if (i >= j)
return 0;
// If both end characters are same,
// no insertion is needed.
if (s.charAt(i) == s.charAt(j))
return minInsertions(s, i + 1, j - 1);
// Otherwise, insert at either end
// and choose the better option.
return 1
+ Math.min(minInsertions(s, i + 1, j),
minInsertions(s, i, j - 1));
}
boolean isPossible(String s, int k)
{
// Find the minimum insertions required.
int req = minInsertions(s, 0, s.length() - 1);
// Check whether exactly k insertions are possible.
return req <= k;
}
public static void main(String[] args)
{
GFG m = new GFG();
String s = "abac";
int k = 2;
System.out.println(m.isPossible(s, k) ? "true"
: "false");
}
}
def minInsertions(s, i, j):
# Base case: Empty string or single character.
if i >= j:
return 0
# If both end characters are same,
# no insertion is needed.
if s[i] == s[j]:
return minInsertions(s, i + 1, j - 1)
# Otherwise, insert at either end
# and choose the better option.
return 1 + min(minInsertions(s, i + 1, j), minInsertions(s, i, j - 1))
def isPossible(s, k):
# Find the minimum insertions required.
req = minInsertions(s, 0, len(s) - 1)
# Check whether exactly k insertions are possible.
return req <= k
if __name__ == '__main__':
s = "abac"
k = 2
print("true" if isPossible(s, k) else "false")
using System;
public class GFG {
public int minInsertions(string s, int i, int j)
{
// Base case: Empty string or single character.
if (i >= j)
return 0;
// If both end characters are same,
// no insertion is needed.
if (s[i] == s[j])
return minInsertions(s, i + 1, j - 1);
// Otherwise, insert at either end
// and choose the better option.
return 1
+ Math.Min(minInsertions(s, i + 1, j),
minInsertions(s, i, j - 1));
}
public bool isPossible(string s, int k)
{
// Find the minimum insertions required.
int req = minInsertions(s, 0, s.Length - 1);
// Check whether exactly k insertions are possible.
return req <= k;
}
public static void Main()
{
GFG p = new GFG();
string s = "abac";
int k = 2;
Console.WriteLine(p.isPossible(s, k) ? "true"
: "false");
}
}
function minInsertions(s, i, j)
{
// Base case: Empty string or single character.
if (i >= j)
return 0;
// If both end characters are same,
// no insertion is needed.
if (s[i] === s[j])
return minInsertions(s, i + 1, j - 1);
// Otherwise, insert at either end
// and choose the better option.
return 1
+ Math.min(minInsertions(s, i + 1, j),
minInsertions(s, i, j - 1));
}
function isPossible(s, k)
{
// Find the minimum insertions required.
let req = minInsertions(s, 0, s.length - 1);
// Check whether exactly k insertions are possible.
return req <= k;
}
// Driver Code
let s = "abac";
let k = 2;
console.log(isPossible(s, k) ? "true" : "false");
Output
true
[Better Approach] Using Dynamic Programming (Minimum Insertions DP) - O(n ^ 2) Time and O(n ^ 2) Space
The idea is to use dynamic programming to compute the minimum insertions required for every substring. Build the solution from shorter substrings to longer ones. If the minimum insertions required are at most
k, then exactlykinsertions are also possible by performing any remaining insertions without breaking the palindrome.
Working of the Approach:
- Create a DP table dp, where dp[i][j] stores the minimum insertions required to make the substring s[i...j] a palindrome.
- Process all substrings in increasing order of their lengths, so that the results for smaller substrings are already available.
- If the characters at both ends are the same, no new insertion is needed and copy the answer from the inner substring: dp[i][j] = dp[i + 1][j - 1].
- Otherwise, insert a matching character at either end and take the better choice: dp[i][j] = 1 + min(dp[i + 1][j], dp[i][j - 1]).
- Finally, if dp[0][n - 1] <= k, return true; otherwise, return false.
#include <string>
#include <iostream>
#include <vector>
using namespace std;
bool isPossible(string &s, int k) {
int n = s.size();
// dp[i][j] stores the minimum insertions needed
// to make substring s[i...j] a palindrome.
vector<vector<int>> dp(n, vector<int>(n, 0));
// Consider all substring lengths.
for (int len = 2; len <= n; len++) {
for (int i = 0; i + len - 1 < n; i++) {
int j = i + len - 1;
// If both characters are same,
// no insertion is needed.
if (s[i] == s[j]) {
dp[i][j] = (len == 2) ? 0 : dp[i + 1][j - 1];
}
// Otherwise, insert at either end
// and take the minimum.
else {
dp[i][j] = 1 + min(dp[i + 1][j],
dp[i][j - 1]);
}
}
}
// Check whether exactly k insertions are possible.
return dp[0][n - 1] <= k;
}
int main() {
string s = "abac";
int k = 2;
cout << (isPossible(s, k) ? "true" : "false");
return 0;
}
import java.util.Arrays;
public class GFG {
public static boolean isPossible(String s, int k)
{
int n = s.length();
// dp[i][j] stores the minimum insertions needed
// to make substring s[i...j] a palindrome.
int[][] dp = new int[n][n];
// Consider all substring lengths.
for (int len = 2; len <= n; len++) {
for (int i = 0; i + len - 1 < n; i++) {
int j = i + len - 1;
// If both characters are same,
// no insertion is needed.
if (s.charAt(i) == s.charAt(j)) {
dp[i][j]
= (len == 2) ? 0 : dp[i + 1][j - 1];
}
// Otherwise, insert at either end
// and take the minimum.
else {
dp[i][j] = 1
+ Math.min(dp[i + 1][j],
dp[i][j - 1]);
}
}
}
// Check whether exactly k insertions are possible.
return dp[0][n - 1] <= k;
}
public static void main(String[] args)
{
String s = "abac";
int k = 2;
System.out.println(isPossible(s, k) ? "true"
: "false");
}
}
def isPossible(s, k):
n = len(s)
# dp[i][j] stores the minimum insertions needed
# to make substring s[i...j] a palindrome.
dp = [[0] * n for _ in range(n)]
# Consider all substring lengths.
for length in range(2, n + 1):
for i in range(n - length + 1):
j = i + length - 1
# If both characters are same,
# no insertion is needed.
if s[i] == s[j]:
dp[i][j] = 0 if length == 2 else dp[i + 1][j - 1]
# Otherwise, insert at either end
# and take the minimum.
else:
dp[i][j] = 1 + min(dp[i + 1][j], dp[i][j - 1])
# Check whether exactly k insertions are possible.
return dp[0][n - 1] <= k
if __name__ == "__main__":
s = "abac"
k = 2
print("true" if isPossible(s, k) else "false")
using System;
public class GFG {
public static bool isPossible(string s, int k)
{
int n = s.Length;
// dp[i][j] stores the minimum insertions needed
// to make substring s[i...j] a palindrome.
int[, ] dp = new int[n, n];
// Consider all substring lengths.
for (int len = 2; len <= n; len++) {
for (int i = 0; i + len - 1 < n; i++) {
int j = i + len - 1;
// If both characters are same,
// no insertion is needed.
if (s[i] == s[j]) {
dp[i, j]
= (len == 2) ? 0 : dp[i + 1, j - 1];
}
// Otherwise, insert at either end
// and take the minimum.
else {
dp[i, j] = 1
+ Math.Min(dp[i + 1, j],
dp[i, j - 1]);
}
}
}
// Check whether exactly k insertions are possible.
return dp[0, n - 1] <= k;
}
public static void Main()
{
string s = "abac";
int k = 2;
Console.WriteLine(isPossible(s, k) ? "true"
: "false");
}
}
function isPossible(s, k)
{
const n = s.length;
// dp[i][j] stores the minimum insertions needed
// to make substring s[i...j] a palindrome.
const dp
= Array.from({length : n}, () => Array(n).fill(0));
// Consider all substring lengths.
for (let len = 2; len <= n; len++) {
for (let i = 0; i + len - 1 < n; i++) {
const j = i + len - 1;
// If both characters are same,
// no insertion is needed.
if (s[i] === s[j]) {
dp[i][j]
= (len === 2) ? 0 : dp[i + 1][j - 1];
}
// Otherwise, insert at either end
// and take the minimum.
else {
dp[i][j] = 1
+ Math.min(dp[i + 1][j],
dp[i][j - 1]);
}
}
}
// Check whether exactly k insertions are possible.
return dp[0][n - 1] <= k;
}
// Driver Code
const s = "abac";
const k = 2;
console.log(isPossible(s, k) ? "true" : "false");
Output
true
[Expected Approach] Using Longest Palindromic Subsequence (LPS) - O(n ^ 2) Time and O(n) Space
The idea is to first find the Longest Palindromic Subsequence (LPS) of the string. Since the minimum insertions required equals
n − LPS, compute the LPS using the LCS of the string and its reverse with space-optimized dynamic programming. If the minimum insertions required are at mostk, then exactlykinsertions are also possible by performing any remaining insertions without breaking the palindrome.
Let us understand with an example:
Input: s = "abac", k = 2
- Consider s = "abac" and k = 2. Reverse the string to get "caba" and compute the LCS between "abac" and "caba".
- The LCS length is 3 (one such subsequence is "aba"), which is also the Longest Palindromic Subsequence (LPS).
- The minimum insertions required are n - LPS = 4 - 3 = 1.
- Since only 1 insertion is required and k = 2, the remaining insertion can be performed without breaking the palindrome.
- Therefore, 1 ≤ 2, so the function returns true.
#include <string>
#include <iostream>
#include <vector>
using namespace std;
int lps(string &s)
{
string rev = s;
reverse(rev.begin(), rev.end());
int n = s.size();
vector<int> prev(n + 1, 0), curr(n + 1, 0);
// Compute the LCS of the string and its reverse
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
if (s[i - 1] == rev[j - 1])
{
curr[j] = 1 + prev[j - 1];
}
else
{
curr[j] = max(curr[j - 1], prev[j]);
}
}
// Move to the next DP row
swap(prev, curr);
}
return prev[n];
}
bool isPossible(string &s, int k)
{
// Find the length of the longest palindromic subsequence
int lps1 = lps(s);
// Minimum insertions required to make the string a palindrome
int req = s.size() - lps1;
// Check if the required insertions do not exceed k
return req <= k;
}
int main()
{
string s = "abac";
int k = 2;
cout << (isPossible(s, k) ? "true" : "false");
return 0;
}
import java.util.Arrays;
public class GFG {
public static int lps(String s)
{
String rev
= new StringBuilder(s).reverse().toString();
int n = s.length();
int[] prev = new int[n + 1];
int[] curr = new int[n + 1];
// Compute the LCS of the string and its reverse
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (s.charAt(i - 1) == rev.charAt(j - 1)) {
curr[j] = 1 + prev[j - 1];
}
else {
curr[j]
= Math.max(curr[j - 1], prev[j]);
}
}
// Move to the next DP row
int[] temp = prev;
prev = curr;
curr = temp;
}
return prev[n];
}
public static boolean isPossible(String s, int k)
{
// Find the length of the longest palindromic
// subsequence
int lps1 = lps(s);
// Minimum insertions required to make the string a
// palindrome
int req = s.length() - lps1;
// Check if the required insertions do not exceed k
return req <= k;
}
public static void main(String[] args)
{
String s = "abac";
int k = 2;
System.out.println(isPossible(s, k) ? "true"
: "false");
}
}
def lps(s):
rev = s[::-1]
n = len(s)
prev = [0] * (n + 1)
curr = [0] * (n + 1)
# Compute the LCS of the string and its reverse
for i in range(1, n + 1):
for j in range(1, n + 1):
if s[i - 1] == rev[j - 1]:
curr[j] = 1 + prev[j - 1]
else:
curr[j] = max(curr[j - 1], prev[j])
# Move to the next DP row
prev, curr = curr, prev
return prev[n]
def isPossible(s, k):
# Find the length of the longest palindromic subsequence
lps1 = lps(s)
# Minimum insertions required to make the string a palindrome
req = len(s) - lps1
# Check if the required insertions do not exceed k
return req <= k
if __name__ == "__main__":
s = "abac"
k = 2
print("true" if isPossible(s, k) else "false")
using System;
using System.Linq;
public class GFG {
public static int lps(string s)
{
string rev = new string(s.Reverse().ToArray());
int n = s.Length;
int[] prev = new int[n + 1];
int[] curr = new int[n + 1];
// Compute the LCS of the string and its reverse
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (s[i - 1] == rev[j - 1]) {
curr[j] = 1 + prev[j - 1];
}
else {
curr[j]
= Math.Max(curr[j - 1], prev[j]);
}
}
// Move to the next DP row
int[] temp = prev;
prev = curr;
curr = temp;
}
return prev[n];
}
public static bool isPossible(string s, int k)
{
// Find the length of the longest palindromic
// subsequence
int lps1 = lps(s);
// Minimum insertions required to make the string a
// palindrome
int req = s.Length - lps1;
// Check if the required insertions do not exceed k
return req <= k;
}
public static void Main()
{
string s = "abac";
int k = 2;
Console.WriteLine(isPossible(s, k) ? "true"
: "false");
}
}
function lps(s)
{
let rev = s.split("").reverse().join("");
let n = s.length;
let prev = Array(n + 1).fill(0);
let curr = Array(n + 1).fill(0);
// Compute the LCS of the string and its reverse
for (let i = 1; i <= n; i++) {
for (let j = 1; j <= n; j++) {
if (s[i - 1] === rev[j - 1]) {
curr[j] = 1 + prev[j - 1];
}
else {
curr[j] = Math.max(curr[j - 1], prev[j]);
}
}
// Move to the next DP row
[prev, curr] = [ curr, prev ];
}
return prev[n];
}
function isPossible(s, k)
{
// Find the length of the longest palindromic
// subsequence
let lps1 = lps(s);
// Minimum insertions required to make the string a
// palindrome
let req = s.length - lps1;
// Check if the required insertions do not exceed k
return req <= k;
}
// Driver Code
let s = "abac";
let k = 2;
console.log(isPossible(s, k) ? "true" : "false");
Output
true