最近刷算法题的时候遇到了这么一道题,自己一直发现不了错误,然后题解思路和我的也不一样,需要用KMP算法,看不明白,通过MARSCODE AI帮助我读懂了题解,找到了问题所在!!
首先来读题:
小秋很喜欢在某平台上看一些商家推荐。
一天小秋看到有一家商铺的好评非常多,想去体验一下,于是他高高兴兴地去了那家商铺。不出意外的话还是出意外了。那家商铺对小秋来说简直是噩梦,和平台上的好评完全相反。小秋认为这个商家的评论有问题,很可能是通过某种刷评论软件刷的。
在反复查看商家的评论后,小秋发现,在后面的评论中,经常出现和前面一模一样的评论。
因此,小秋做了如下推断:假设把商家现在的评论看成一个只包含小写字母的字符串 s,s 是通过一个空字符串 t,经过若干次下列操作得到的:
- 在 t 末尾添加任意一个小写字母;
- 在 t 末尾添加 t 任意长度的前缀;
小秋现在想知道,对于一个商家的评论 s,由一个空字符串得到最少需要多少次上述操作。
如果计算得到的操作数量过小,那么商家的评论肯定就是刷的,就可以去举报了。你只需要帮小秋求出这个最小操作数量。
输入格式
输入仅一行,一个只包含小写字母的字符串 s。
输出格式
输出仅一行,一个整数表示由空字符串得到 s 的最少操作数量。
样例输入
ababa
样例输出
4
说明
- 第 1 次操作,在字符串末尾添加一个字符 a,得到字符串 a;
- 第 2 次操作,在字符串末尾添加一个字符 b,得到字符串 ab;
- 第 3 次操作,在字符串末尾添加前缀 ab,得到字符串 abab;
- 第 4 次操作,在字符串末尾添加一个字符 a,得到字符串 ababa。
评测数据规模
对于 20% 的评测数据,1≤∣s∣≤103
对于 40% 的评测数据,1≤∣s∣≤105
对于 100% 的评测数据,1≤∣s∣≤106
我一开始的思路:
一个个遍历字符,然后找前缀的第0个字符中是否为该字符。
如果是,继续找遍历位置的下一字符是否出现在第二个字符处...。
这样依次往后找,直到找到不相等处或者找到前缀的最后一个字母处。
这些相等的字符采用操作2来一次完成。
import os
import sys
x=input()
n=len(x)
cnt=1
i=1
while i<n:
if x[i] != x[0]:
cnt+=1
i+=1
else:
temp=1
while x[i+temp] == x[temp]:
temp+=1
if i+temp>=n:
break
if temp>=i:
break
cnt+=1
i=i+temp
#print(cnt,i-1)
print(cnt)
但是这样只能通过60%的样例,让我们来看一下MARSCODE AI的说明:
你的代码逻辑是正确的,但是在处理字符串索引时可能会出现越界的情况。在Python中,字符串的索引是从0开始的,因此当你访问x[i+temp]时,如果i+temp的值大于等于字符串x的长度n,就会导致IndexError。
为了避免这种情况,你可以在访问字符串索引之前检查索引是否越界。
MARSCODE AI的讲解一开始并不正确,但是在学习了题解后,它又对回复进行了改进:
在 KMP 算法中,next 数组用于记录字符串中每个位置之前的最长相等前缀和后缀的长度。在这个问题中,我们可以利用 next 数组来找到字符串中可以通过复制前缀得到的最长重复子串。
接下来,我们考虑动态规划(DP)的方法来解决这个问题。我们定义一个数组 dp,其中 dp[i] 表示得到字符串 s 的前 i 个字符的最小操作次数。
对于每个位置 i,我们有两种可能的操作:
- 在字符串
t的末尾添加一个任意小写字母,这将使操作次数增加1。因此,dp[i] = dp[i-1] + 1。 - 复制字符串
t的某个前缀到末尾。为了找到这个前缀,我们需要查看next数组。假设next[i]表示字符串s的前i个字符中最长相等前缀和后缀的长度,那么我们可以从位置i - next[i]复制前缀到位置i。但是,我们需要注意一个问题:如果复制的前缀长度小于等于i的一半,那么我们可能无法通过一次复制操作得到长度为i的字符串。因此,我们需要找到一个最长的不重叠的相等前缀和后缀,即min(⌊i/2⌋, next[i])。这样,我们的转移方程为dp[i] = min(dp[i-1], dp[i - min(⌊i/2⌋, next[i])]) + 1。
最终,我们的答案是 dp[n],其中 n 是字符串 s 的长度。
最后,我理解到了我的代码哪里出问题了。
我的思路是贪心方法,但是动态递归才是正解。
比如对于aakakaaakak字符串,
若利用我最开始的代码(贪心):是a a k a k aa a k a k这样分割,
而利用动态规划:是a a k a k a aakak这样分割!!
次数很明显是dp的更少
下面给出蓝桥云课的官方AC Code:
import sys
def main():
s = input()
n = len(s)
s = " " + s
ne = [0] * (n + 1)
for i in range(2, n + 1):
j = ne[i - 1]
while s[i] != s[j + 1] and j:
j = ne[j]
if s[j + 1] == s[i]:
j += 1
ne[i] = j
f = [1000000000] * (n + 1)
f[0] = 0
for i in range(1, n + 1):
f[i] = f[i - 1] + 1
length = min(i // 2, ne[i])
f[i] = min(f[i], f[i - length] + 1)
print(f[n])
if __name__ == "__main__":
main()