LeetCode 2430. 对字母串可执行的最大删除数
给你一个仅由小写英文字母组成的字符串 s 。在一步操作中,你可以:
- 删除 整个字符串 s ,或者
- 对于满足 1 < = i < = s . l e n g t h / 2 1 <= i <= s.length / 2 1<=i<=s.length/2 的任意 i ,如果 s 中的 前 i 个字母和接下来的 i 个字母 相等 ,删除 前 i 个字母。
例如,如果 s = “ababc” ,那么在一步操作中,你可以删除 s 的前两个字母得到 “abc” ,因为 s 的前两个字母和接下来的两个字母都等于 “ab” 。
返回删除 s 所需的最大操作数。
提示:
- 1 < = s . l e n g t h < = 4000 1 <= s.length <= 4000 1<=s.length<=4000
- s 仅由小写英文字母组成
思路
首先一个字符串至少可以通过一次操作使其变成空串。那么如果需要最大化操作次数,就需要尽可能多的使用操作2。如果当前字符串s的某个前缀s[1,i]=s[i+1,2*i]相同的话,那么可以通过操作2来删除前i个字符。可以对于字符串s,可能存在多种方案使其删除某个前缀,但是对于每种方案都可能对后续产生不同的影响,所以此时使用记忆化搜索来求解。但对于每一次记忆化搜索,都需要在 O ( 1 ) O(1) O(1)的时间复杂度下快速判断某个前缀子字符串与后续的子字符串是否相等,此时可以使用字符串哈希来快速判断。字符串哈希就是将字符串当作一个P进制数,然后将其转化为10进制数来计算某个前缀的哈希值。计算某个子字符串s[i,j]的哈希值时,可通过 f [ j ] − f [ i − 1 ] ∗ P j − i + 1 f[j]-f[i-1]*P^{j-i+1} f[j]−f[i−1]∗Pj−i+1来快速计算,从而快速实现字符串的比较。
代码
class Solution {
long[] f, p;
int P = 131;
int n;
int[] ff;
public int deleteString(String s) {
n = s.length();
f = new long[n+1];
p = new long[n+1];
ff = new int[n];
init(s);
return dfs(s,0);
}
public void init(String s) {
p[0] = 1;
for(int i = 1; i <= n; i++) {
f[i] = f[i-1] * P + s.charAt(i-1);
p[i] = p[i-1] * P;
}
}
public int dfs(String s, int u) {
if(u == n) {
return 0;
}
if(ff[u] != 0) {
return ff[u];
}
int len = n - u;
ff[u] = 1;
for(int i = 1; i <= len / 2; i++) {
if(get(u+1,u+i) == get(u+i+1,u+2*i)) {
ff[u] = Math.max(ff[u], dfs(s,u+i) + 1);
}
}
return ff[u];
}
public long get(int a, int b) {
return f[b] - f[a-1] * p[b-a+1];
}
}