思路:每加入一个新的字符,更新当前串所有的后缀。
1.单调性:沿着fail指针走,出现位置变多
2.不考虑 cur clone 出现的位置与q一样
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 300005;
int righ[maxn<<1];
const int ch_size = 26;
struct SAM{
int fa[maxn<<1], // 后缀链接
ch[maxn<<1][ch_size],
len[maxn<<1], //该节点最长串的长度
tot, // 节点总数
last; // 代表当前的整个串
void init(){
tot = last = 0;
fa[0] = -1;
len[0] = 0;
memset( ch[0],0,sizeof( ch[0] ) );
}
void extend( int x ){
int p = last;
int cur = ++tot;
memset( ch[cur],0,sizeof( ch[cur] ) );
len[cur] = len[last]+1;
while( p != -1 && !ch[p][x] ){
ch[p][x] = cur;
p = fa[p];
}
if( p == -1 ){
fa[cur] = 0;
}else {
int q = ch[p][x];
if (len[q] == len[p] + 1) {
fa[cur] = q;
} else {
int clone = ++tot;
righ[clone] = righ[ q ]; // 划重点!!! clone 出现的位置集合与q是一样的(暂时不考虑加入的cur)
len[clone] = len[p] + 1;
for (int i = 0; i < ch_size; i++) {
ch[clone][i] = ch[q][i];
}
fa[clone] = fa[q];
fa[q] = fa[cur] = clone;
while (p != -1) {
if (ch[p][x] == q)ch[p][x] = clone;
else break;
p = fa[p];
}
}
}
last = cur;
}
}g;
char str[maxn];
int k;
LL ans;
void solve(int x){
while( x ){
if( righ[x] >= k ) return;
righ[x]++;
if( righ[x]>=k ){
ans += g.len[x] - g.len[ g.fa[x] ];
}
x = g.fa[x];
}
}
int main(){
int n,m;
while(3==scanf("%d%d%d",&n,&m,&k)) {
scanf("%s", str);
g.init();ans =0;
for (int i = 0; i < n; i++) {
g.extend(str[i] - 'a');
solve( g.last );
}
int op;
char ch[5];
for (int i = 0; i < m; i++) {
scanf("%d", &op);
if (op == 1) {
scanf("%s",ch);
g.extend(ch[0] - 'a');
solve(g.last);
} else {
printf("%lld\n", ans);
}
}
for( int i = 0;i <= g.tot;i++ ) righ[i] = 0;
}
return 0;
};