Description:
Input:
第一行:包含一个整数N。
第二行:包含一个长度为N的字符串,字符串中只包含小写字母。
第三行:包含一个长度为N的字符串,字符串中只包含小写字母。
Output:
输出答案只包含一个数字L,表示圆环最大可能有的格子数。
Sample Input:
输入1:
5
abcdx
cdabz
输入2:
4
abcd
cdab
Sample Output:
输出1:
4
输出2:
4
Data Constraint:
对于20% 的数据,1 <= N <= 5,000
对于50% 的数据,1 <= N <= 600,000
对于100% 的数据,1 <= N <= 2,000,000
题目大意:
给出两个长度为n的字符串a,b,求最大的L,使得a[1..L]和b[1..L]是循环同构的(展开后是一个同一个环)。
1 <= n <= 2000000
题解:
算是exkmp比较裸的题了。
设exkmpA[i]表示a[i..n]与b的lcp。
exkmpB同理。
我们枚举a串的一个位置k,那我们需要在exkmpB[1..exkmpA[k] +1]中找到一个最大的j,使得exkmpB[j] >= k。
exkmpA、exkmpB是很好求的。
关键在于如何找到j。
我们很容易想到线段树维护一个exkmpB的最大值,然后使用线段树二分。
可是不要忘记n是2000000.
O(n logn)只有50分。
由于我们可以从左到右枚举k,我们想象一个数组c,c[i]表示exkmpB[i]是否大于等于当前k,要找到最大的j,就相当于找c[exkmpA[k]+1]往左边(可以是它本身)的第一个1,这个东西我们可以用并查集维护,至于c[i]的修改用前向星解决。
JZOJ的栈没良心,逼得我打了人工栈并查集。
Code:
#include<cstdio>
#include<cstring>
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))
using namespace std;
const int Maxn = 2000005;
int n, ans; char a[Maxn], b[Maxn];
int next[Maxn], exkmp[Maxn];
int f[Maxn], z[Maxn], bz[Maxn];
int final[Maxn], nt[Maxn], to[Maxn], tot;
int find(int x) {
z[++ z[0]] = x; bz[1] = 0;
while(z[0]) {
if(!bz[z[0]]) {
if(f[z[z[0]]] == z[z[0]]) {
z[0] --;
} else {
bz[z[0]] = 1; z[++ z[0]] = f[z[z[0] - 1]]; bz[z[0]] = 0;
}
} else f[z[z[0]]] = f[z[z[0] + 1]], z[0] --;
}
return f[x];
}
void link(int x, int y) {
nt[++ tot] = final[x], to[tot] = y, final[x] = tot;
}
void Get_next(char s[]) {
next[1] = n;
int a = 1; while(a < n && s[a + 1] == s[a]) a ++;
next[2] = a - 1, a = 2;
fo(i, 3, n) {
int p = a + next[a] - 1, l = next[i - a + 1];
if(i + l - 1 >= p) {
int j = (p - i + 1) > 0 ? (p - i + 1) : 0;
while(i + j - 1 < n && s[j + 1] == s[i + j]) j ++;
next[i] = j, a = i;
} else next[i] = l;
}
}
void Get_exkmp(char s[], char t[]) {
int a = 1; while(a < n && s[a + 1] == t[a + 1]) a ++;
exkmp[1] = a - 1, a = 1;
fo(i, 2, n) {
int p = a + exkmp[a] - 1, l = next[i - a + 1];
if(i + l - 1 >= p) {
int j = (p - i + 1) > 0 ? (p - i + 1) : 0;
while(i + j - 1 < n && j < n && s[i + j] == t[j + 1]) j ++;
exkmp[i] = j, a = i;
} else exkmp[i] = l;
if(i + exkmp[i] - 1 > n)
exkmp[i] = n - i + 1;
}
}
void delet(int x) {
f[x] = find(f[x - 1]);
}
void End() {
fo(i, 1, n) f[i] = i;
fo(i, 1, n) {
int j = find(exkmp[i] + 1);
if(j) ans = max(ans, i + j - 2);
for(int k = final[i]; k; k = nt[k])
delet(to[k]);
}
printf("%d", ans);
}
int main() {
scanf("%d", &n);
scanf("%s", a + 1); scanf("%s", b + 1);
Get_next(a);
Get_exkmp(b, a);
fo(i, 1, n) link(exkmp[i] + 1, i);
Get_next(b);
Get_exkmp(a, b);
End();
}

446

被折叠的 条评论
为什么被折叠?



