GYM-102361J MUV LUV EXTRA kmp求最小循环节
GYM-102361J MUV LUV EXTRA kmp求最小循环节
题意
给定正整数\(a,b\)求最大的\(a \times p - b\times l\),其中\(p\)表示后缀的循环节的总长度,\(l\)表示这个最小循环节的长度
\[1 \leq a,b\leq 1e9\\
1 \leq |s| \leq 1e7
\]
分析
求这个最大值,不妨枚举\(p\),逆序字符串,这样问题就转化成了求最小循环节。
对于求最小循环节(可以在前面补),可以利用KMP求得next数组,长度为\(n\)的字符串的最小循环节长度就是\(n - next[n]\)。
这好像并不显然,不严谨证明如下:
代码
#include<bits/stdc++.h>
#define fi first
#define se second
using namespace std;
typedef long long ll;
ll rd(){
ll x = 0;
int f = 1;
char ch = getchar();
while(ch < '0' || ch > '9') {
if(ch == '-') f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9') {
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
const int maxn = 1e7 + 5;
int Next[maxn];
char s1[maxn],s2[maxn];
int len1,len2;
void getNext(){
for(int i = 2,j = 0;i <= len2;i++){
while(s2[i] != s2[j + 1] && j > 0) j = Next[j];
if(s2[i] == s2[j + 1]) Next[i] = ++j;
}
}
int main(){
ll a,b;
while(~scanf("%lld%lld",&a,&b)){
scanf("%s",s1 + 1);
len1 = strlen(s1 + 1);
int pos = -1;
for(int i = 1;i <= len1;i++){
if(s1[i] == '.') {
pos = i + 1;
break;
}
}
strcpy(s2 + 1,s1 + pos);
len2 = strlen(s2 + 1);
reverse(s2 + 1,s2 + len2 + 1);
getNext();
ll ans = a - b;
for(int i = 1;i <= len2;i++){
ans = max(ans,a * i - b * (i - Next[i]));
}
cout << ans << '\n';
}
}