sam的实际应用之一,用num记录每个节点出现的次数,每次更新一个节点就让他前面的子串加1,因为他的子串都出现了一遍。结果加上当前串的长度减去上个串的长度,因为上一个串已经加过一次了,不能重复进行。
#include <bits/stdc++.h>
#define pb push_back
#define mp make_pair
using namespace std;
typedef long long LL;
const int maxn = 2*1e6 + 8;
string s;
int k;
struct SAM
{
int ch[maxn][26],pre[maxn],val[maxn],endpos[maxn];
//diy
int num[maxn];
//diy
int last, tot;
void init(){
last = tot = 0;
memset(ch[0], -1, sizeof ch[0]);
pre[0] = -1; val[0] = 0;
}
int extend(int c, int ind){
int p = last, np = ++tot;
//diy
num[np]=0;
//diy
val[np] = val[p] + 1; endpos[np] = ind;
memset(ch[np], -1, sizeof ch[np]);
while(~p && ch[p][c] == -1) ch[p][c] = np, p = pre[p];
if(p == -1) pre[np] = 0;
else{
int q = ch[p][c];
if(val[q] != val[p] + 1){
int nq = ++tot;
num[nq]=num[q];
memcpy(ch[nq], ch[q], sizeof ch[q]);
val[nq] = val[p] + 1;
pre[nq] = pre[q];
pre[q] = pre[np] = nq;
while(~p && ch[p][c] == q) ch[p][c] = nq, p = pre[p];
}
else pre[np] = q;
}
last = np;
//diy
while(np!=-1&&num[np]<k)
{
num[np]++;
if(num[np]==k)
return val[np]-val[pre[np]];
np=pre[np];
}
//diy
// return val[np] - val[pre[np]];
return 0;
}
}sam;
int main()
{
int n, i,m,op;
while(~scanf("%d%d%d",&n,&m,&k))
{
LL sum =0;
cin >> s;
n = s.size();
char c;
sam.init();
for(i = 0; i < n; i++) sum += sam.extend(s[i] - 'a', i+1);
while(m--)
{
scanf("%d%*c",&op);
if(op==1){scanf("%c",&c);sum +=sam.extend(c-'a',++n);}
else printf("%I64d\n",sum);
}
//cout << sum << endl;
}
return 0;
}