最小的子串需要被删除才能使给定的字符串成为回文

1. 前言

回文串是指正读和反读都相同的字符串,例如“level”、“racecar”等。在字符串的处理中,回文串是比较重要的一种类型,因为很多问题都可以转化成求最长回文子串或者回文串的个数。

本文将要探讨的问题是:如何删除最小的子串,使给定的字符串成为回文。该问题可以转化为求出最小的需要删除的字符数。本文将介绍两种解决方法:动态规划和中心扩展法。

2. 动态规划

动态规划是求解最长回文子串问题的经典算法。其基本思想是:定义二维数组dp[i][j]表示区间[i,j]是否为回文串,如果是,则dp[i][j]=true,否则dp[i][j]=false。根据这个定义可以得到状态转移方程:

if(s[i]==s[j]) dp[i][j]=dp[i+1][j-1];

else dp[i][j]=min(dp[i+1][j],dp[i][j-1])+1;

其中,如果s[i]==s[j],则区间[i,j]如果是回文串,则区间[i+1,j-1]也必然是回文串,因此可以递推得到dp[i][j]=dp[i+1][j-1];如果s[i]!=s[j],则区间[i,j]必须删除一个字符,才可能成为回文串,因此可以将问题转化为求解区间[i+1,j]和区间[i,j-1]的最小删除次数,即dp[i][j]=min(dp[i+1][j],dp[i][j-1])+1。

2.1 举例说明

例如,对于字符串s="abca",其dp数组的值为:

|   | a | b | c | a |

|---|---|---|---|---|

| a | T | F | F | T |

| b | | T | F | F |

| c | | | T | F |

| a | | | | T |

根据状态转移方程,可以推导出dp[0][3]=1,即需要删除一个字符("b")才能使整个字符串成为回文串。

3. 中心扩展法

中心扩展法是另一种经典的求解回文串问题的算法。其基本思想是:枚举回文串的中心位置,然后向左右两端扩展,直到扩展不下为止。由于回文串长度可能是奇数也可能是偶数,因此需要分别枚举回文串中心为一个字符和两个字符的情况。

3.1 回文串中心为一个字符

假设字符串s的长度为n,那么中心位置一共有2n-1个,因为可能是单个字符,也可能是两个字符的中心。

对于中心位置为i的情况,可以从i向左侧和右侧分别扩展,直到不再为回文串为止。具体做法是,使用两个指针l和r,初始值分别为i和i,然后不断移动指针,直到s[l]!=s[r]或者越界。在移动指针的过程中,每次判断s[l]==s[r],如果成立,则回文串长度加2,否则跳出循环。

3.2 回文串中心为两个字符

假设字符串s的长度为n,那么中心位置一共有2n个,因为必须是两个相邻的字符。

对于中心位置为(i,i+1)的情况,可以从i向左侧,从i+1向右侧分别扩展,直到不再为回文串为止。具体做法是,使用两个指针l和r,初始值分别为i和i+1,然后不断移动指针,直到s[l]!=s[r]或者越界。在移动指针的过程中,每次判断s[l]==s[r],如果成立,则回文串长度加2,否则跳出循环。

3.3 完整代码

class Solution {

public:

int minDelete(string s) {

int n=s.length();

vector<vector<int>> dp(n,vector<int>(n,0));

for(int len=2;len<=n;len++) {

for(int i=0;i<=n-len;i++) {

int j=i+len-1;

if(s[i]==s[j])

dp[i][j]=dp[i+1][j-1];

else

dp[i][j]=min(dp[i+1][j],dp[i][j-1])+1;

}

}

return dp[0][n-1];

}

bool isPalindrome(string s) {

int n=s.length();

for(int i=0;i<n/2;i++) {

if(s[i]!=s[n-1-i])

return false;

}

return true;

}

int minDeletePalindrome(string s) {

if(isPalindrome(s))

return 0;

int n=s.length();

int ans=n-1;

for(int i=0;i<2*n-1;i++) {

int l,r;

if(i%2==0) { // 回文串中心为一个字符的情况

l=r=i/2;

while(l>=0 && r<n && s[l]==s[r]) {

l--;

r++;

}

int len=r-l-1;

if(len%2==0 && len>0) // 如果回文串长度是偶数,则需要删除len/2个字符

ans=min(ans,n-len/2);

else if(len%2==1 && len>0) // 如果回文串长度是奇数,则需要删除(len-1)/2个字符

ans=min(ans,n-(len-1)/2);

} else { // 回文串中心为两个字符的情况

l=i/2;

r=l+1;

while(l>=0 && r<n && s[l]==s[r]) {

l--;

r++;

}

int len=r-l-1;

if(len%2==0 && len>0) // 如果回文串长度是偶数,则需要删除len/2个字符

ans=min(ans,n-len/2);

else if(len%2==1 && len>0) // 如果回文串长度是奇数,则需要删除(len-1)/2个字符

ans=min(ans,n-(len-1)/2);

}

}

return ans;

}

};

4. 总结

本文介绍了两种解决最小删除次数问题的方法:动态规划和中心扩展法。动态规划需要用二维数组表示状态,并且需要计算所有状态的值,因此时间复杂度为O(n^2)。中心扩展法的时间复杂度为O(n^2),但是常数较小,因此对于一些小的实例,中心扩展法可能更加高效。此外,中心扩展法的思想可以应用到许多其他问题中,例如求最长回文子串、回文串个数等,具有广泛的应用价值。

总的来说,最小删除次数问题是字符串处理中比较典型的问题,解决起来比较困难。本文介绍的两种方法各有优缺点,可以根据具体情况选择合适的算法进行求解。

后端开发标签