0
点赞
收藏
分享

微信扫一扫

【LeetCode-6195. hard】对字母串可执行的最大删除数


题干:​​力扣​​

【LeetCode-6195. hard】对字母串可执行的最大删除数_算法

 

【LeetCode-6195. hard】对字母串可执行的最大删除数_算法_02

解题报告:

刚开始觉得直接贪心选有短则短,但是发现不行,不能贪心有短的可以操作的则选短的。

三个方式想到倒着递推。一是直接记住这个特例,正推还倒推不能互相转换的特例。

二是因为dp[i]代表前i个的最大次数,这其实不只要保存最大次数这一个状态,还有可能很多局部最优解需要保存。怎么消除这个因素呢?就是怎么让他和子状态无关呢?倒着推,结合这题的题意,正好。

三是正难则反的思想。正着想是,dp[i]代表前i个能消除的最大次数,枚举最后一次消除次数,反着想,就是dp[i]代表从i到结尾都消除的最大次数,然后枚举第一次消除的字符串。然后一想,诶,这就是可解的了。

然后就是对于如何判断两个字符串的相等,可以字符串哈希,也可以用求LCP的板子。

AC代码:

class Solution {
public:
unsigned long long H[5005];
unsigned long long p[5005];// 都自然溢出即可
int seed = 31, dp[5005];
bool ok[5005][5005];
unsigned long long cal(int l, int r) {
if (l == 0) return H[r];
return H[r] - H[l-1] * p[r-l+1];
}
int deleteString(string s) {
H[0] = s[0]-'a'+1;
p[0] = 1;
for(int i = 1; i<s.size(); i++) p[i] = p[i-1]*seed;
for(int i = 1; i<s.size(); i++) {
H[i] = H[i-1]*seed + s[i] - 'a' + 1;
}
int ans = 0;
int st = 0;

for(int i = 0; i<s.size(); i++) {
for(int j = i+1; j<s.size(); j+=2) {
int len = j-i+1;
if(cal(i, i+len/2-1) == cal(i+len/2, i+len-1)) ok[i][j] = 1;
}
}
for(int i = s.size()-1; i>=0; i--) {
dp[i] = 1;
for(int j = i+1; j<s.size(); j+=2) {
if(ok[i][j]) dp[i] = max(dp[i], dp[i+(j-i+1)/2] + 1);
}
}
return dp[0];
}
};

错误代码:(从前往后递推的)

class Solution {
public:
unsigned long long H[5005];
unsigned long long p[5005];// 都自然溢出即可
int seed = 31, dp[5005];
bool ok[5005][5005];
unsigned long long cal(int l, int r) {
if (l == 0) return H[r];
return H[r] - H[l-1] * p[r-l+1];
}
int deleteString(string s) {
H[0] = s[0]-'a'+1;
p[0] = 1;
for(int i = 1; i<s.size(); i++) p[i] = p[i-1]*seed;
for(int i = 1; i<s.size(); i++) {
H[i] = H[i-1]*seed + s[i] - 'a' + 1;
}
int ans = 0;
int st = 0;

for(int i = 0; i<s.size(); i++) {
for(int j = i+1; j<s.size(); j+=2) {
int len = j-i+1;
if(cal(i, i+len/2-1) == cal(i+len/2, i+len-1)) ok[i][j] = 1;
}
}
for(int i = 0; i<s.size(); i++) {
dp[i] = -1;
if(ok[0][i]) dp[i] = 1;
for(int j = 1; j<i; j++) {
if(i+i-j >= s.size()) continue;

if(dp[j] != -1 && ok[j+1][i+i-j]) {
dp[i] = max(dp[i], dp[j]+1);
}
}
if(i != s.size()-1) ans = max(ans, dp[i]);
}
return max(ans+1, dp[s.size()-1]);
}
};

LCP:

lcp[i][j] 表示 s[i:] 和 s[j:] 的最长公共前缀

n = len(s)
lcp = [[0] * (n + 1) for _ in range(n + 1)] # lcp[i][j] 表示 s[i:] 和 s[j:] 的最长公共前缀
for i in range(n - 1, -1, -1):
for j in range(n - 1, i, -1):
if s[i] == s[j]:
lcp[i][j] = lcp[i + 1][j + 1] + 1

举报

相关推荐

0 条评论