http://acm.hdu.edu.cn/contests/contest_showproblem.php?cid=872&pid=1010
题意可以转化为:给你一个字符串
S
S
S,
O
(
n
)
O(n)
O(n)求每一个后缀的最短循环节(最后几位可以不完整,比如1234123412的最短循环节是1234,最后的12不完整但和循环节前2个匹配)。
思路:
其实和kmp求完整循环节差不多。
先把字符串反转一下,就转化为了求每一个前缀的最短循环节。
i
−
n
e
x
t
(
i
)
i-next(i)
i−next(i)就是前
i
i
i个字符的最短循环节,为什么呢?比如原来是1234123412,反转后变成2143214321,我们的本意是
[
21
]
[
4321
]
[
4321
]
[21][4321][4321]
[21][4321][4321],但是,分组偏移一下是一样的!即
[
2143
]
[
2143
]
[
21
]
[2143][2143][21]
[2143][2143][21]。
而对于前缀找循环节,简单假证一下,令
k
=
=
i
−
n
e
x
t
(
i
)
k==i-next(i)
k==i−next(i),那么
s
(
i
)
=
=
s
(
i
+
k
)
s(i)==s(i+k)
s(i)==s(i+k),这样,把字符串s划分为了
k
k
k个等价类,取相邻的
k
k
k个为一组,那么
i
/
k
i/k
i/k就是完整的循环节个数,
i
%
k
i\%k
i%k是不完整但匹配的部分长度。
代码如下,注意ans初始化为-INF,而不是0。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=10000000+100;
ll a,b,ans;
int f[maxn];
char s[maxn];
int main()
{
//freopen("input.in","r",stdin);
while(cin>>a>>b)
{
ans=-(1LL<<60);
char c=0;
while(c!='.')c=getchar();
scanf("%s",s);
int len=strlen(s);
for(int i=0;i<len/2;i++)swap(s[i],s[len-1-i]);
for(int i=1;i<len;i++)
{
int j=f[i];
while(j && s[i]!=s[j])j=f[j];
f[i+1]=(s[i]==s[j]?j+1:0);
}
for(int i=len;i>=1;i--)
{
ans=max(ans,a*i-b*(i-f[i]));
}
cout<<ans<<endl;
}
return 0;
}