【牛客网】白兔的字符串(扩展kmp+hash+最小表示法)
【牛客网】15253 白兔的字符串
比较两个字符串是否循环同构从比较两个最小表示是否相同开始
我们把T转成循环同构,这不影响答案
然后我们要假定T真的是\(S\)长度为\(len(T)\)区间的一个最小表示,对于这个区间设为\([l,r]\),我们设另一个在\(S\)上的指针为\(p\),如果\(p\)匹配到了区间的末尾,那可以认为\(p\)开头的区间的一个循环同构表示法“可能是最小的”(任意一个后缀\([p',r]\)不会大于\([p,r]\)长度相等的前缀部分,因为\([p,r]\)匹配了\(T\)的一个前缀,\(T\)转化成了最小表示),这个时候只要用\([l,p - 1]\)和\(T\)的相应长度的后缀比较一下是否相等即可
这里考虑以下为什么当\([p,r]\)可以匹配前缀,存在\(p' > p\),\([p',r]\)也可以匹配前缀,为什么不会出现错误
因为我们假定了这个区间是循环同构\(T\)的,而只要它等于T,则\(p\)必然是区间循环同构的开头,而\(p'\)也可能是循环同构的开头,所以找谁不重要
而不等于\(T\),这个性质也没有什么作用,\(p\)是不是最小表示法的开头都无所谓
如果发现\(p\)开始的循环表示是比\(T\)严格大的一个串,类似与最小表示法的算法这一整段区间都可以跳过
如果是比\(T\)严格小呢?这个区间肯定是废了(不可能统计进答案了),但是为了编码的统一性,让\(p\)继续右移找到后缀匹配\(T\)的前缀可以保证操作的复杂度正确。为了复杂度正确,如果匹配了\(k\)长度的串,让\(p\)直接跳到\(p + k\)似乎不错,复杂度对了,但是这显然有问题
那么这既然是\([p,p + k - 1]\)是\(T\)的一段前缀,扩展kmp正好可以处理所有位置开始的后缀和前缀匹配长度,所以先对T跑一遍扩展kmp,然后对\([p,p + k - 1]\)的每一个位置判断能否满足\([p',p + k - 1]\)也匹配\(T\)的开头,让过程继续下去
如果\(p = l\),下一个区间是\([l + 1,r + 1]\),需要用类似的操作右移一下\(p\)
比较两个字符串是否相同可以哈希,防止冲突可以来双哈希
#include <bits/stdc++.h>
#define fi first
#define se second
#define mo 99994711
//#define ivorysi
#define enter putchar('\n')
#define space putchar(' ')
#define pii pair<int,int>
typedef long long int64;
using namespace std;
template<class T>
void read(T &res) {
res = 0;T f = 1;char c = getchar();
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
res = res * 10 + c - '0';
c = getchar();
}
res *= f;
}
template<class T>
void out(T x) {
if(x < 0) {x = -x;putchar('-');}
if(x >= 10) out(x / 10);
putchar('0' + x % 10);
}
int mod[2] = {99994711,99977797};
int ba[2] = {91,47};
int e[2][2000006];
struct Hash {
int hsh[2][2000006];
void Init(char *t,int lenT) {
for(int i = 1 ; i <= lenT ; ++i) {
for(int j = 0 ; j <= 1 ; ++j) {
hsh[j][i] = 1LL * e[j][i] * (t[i] - 'a' + 1) % mod[j];
hsh[j][i] = hsh[j][i] + hsh[j][i - 1];
if(hsh[j][i] >= mod[j]) hsh[j][i] -= mod[j];
}
}
}
pair<int,int> Query(int l,int r) {
int res[2] = {0,0};
for(int j = 0 ; j <= 1; ++j) {
res[j] = hsh[j][r] - hsh[j][l - 1] + mod[j];
if(res[j] >= mod[j]) res[j] -= mod[j];
res[j] = 1LL * res[j] * e[j][1000000 - l] % mod[j];
}
return make_pair(res[0],res[1]);
}
}hsh[2];
char T[2000006],S[2000006];
int N,lenT,lenS,exd[2000005];
void cycle(char *t,int st,int len) {
static char tmp[1000006];
int cnt = 0;
for(int i = st ; i <= len ; ++i) tmp[++cnt] = t[i];
for(int i = 1 ; i < st ; ++i) tmp[++cnt] = t[i];
for(int i = 1 ; i <= len ; ++i) t[i] = tmp[i];
}
bool check(int l1,int r1,int l2,int r2) {
if(r1 < l1) return true;
pair<int,int> a,b;
a = hsh[1].Query(l1,r1);b = hsh[0].Query(l2,r2);
return a == b;
}
void Init() {
scanf("%s",T + 1);
lenT = strlen(T + 1);
for(int i = 1 ; i <= lenT ; ++i) T[i + lenT] = T[i];
int p = 1,q = 2,k = 1;
while(q <= lenT) {
if(T[p + k - 1] == T[q + k - 1]) ++k;
else if(T[p + k - 1] > T[q + k - 1]) {p = max(p + k,q + 1);k = 1;}
else if(T[p + k - 1] < T[q + k - 1]) {q = q + k;k = 1;}
if(p > q) swap(p,q);
}
cycle(T,p,lenT);
e[0][0] = e[1][0] = 1;
for(int i = 1 ; i <= 2000000 ; ++i) {
for(int j = 0 ; j <= 1 ; ++j) {
e[j][i] = 1LL * e[j][i - 1] * ba[j] % mod[j];
}
}
hsh[0].Init(T,lenT);
int maxpos = 0;
for(int i = 2 ; i <= lenT ; ++i) {
if(maxpos) exd[i] = min(exd[i - maxpos + 1],exd[maxpos] + maxpos - i);
while(i + exd[i] <= lenT && T[i + exd[i]] == T[exd[i] + 1]) ++exd[i];
if(exd[i] && i + exd[i] - 1 > maxpos + exd[maxpos] - 1) maxpos = i;
}
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
Init();
read(N);
for(int i = 1 ; i <= N ; ++i) {
scanf("%s",S + 1);
lenS = strlen(S + 1);
hsh[1].Init(S,lenS);
int p = 1,k = 0 ;
int ans = 0;
for(int j = 1 ; j <= lenS - lenT + 1; ++j) {
while(p + k <= j + lenT - 1) {
if(T[k + 1] == S[p + k]) ++k;
else if(T[k + 1] < S[p + k]) {p = p + k + 1;k = 0;}
else if(T[k + 1] > S[p + k]) {
bool f = 0;
for(int h = 2 ; h <= k ; ++h) {
if(h + exd[h] - 1 >= k) {
p = p + h - 1;
k = k - h + 1;
f = 1;
break;
}
}
if(!f) {
if(k != 0) {p = p + k;k = 0;}
else {++p; k = 0;}
}
}
}
//printf("j:%d p:%d k:%d\n",j,p,k);
if(check(j,p - 1,k + 1,lenT)) {
++ans;
//printf("j:%d\n",j);
}
if(p == j) {
for(int h = 2 ; h <= lenT ; ++h) {
if(h + exd[h] - 1 >= lenT) {
p = p + h - 1;
k = j + lenT - p;
break;
}
}
if(p == j) {p = j + lenT;k = 0;}
}
}
out(ans);enter;
}
}