【SAM】bzoj5084: hashit

做得心 力 憔 悴

Description

你有一个字符串S,一开始为空串,要求支持两种操作
在S后面加入字母C
删除S最后一个字母
问每次操作后S有多少个两两不同的连续子串

Input

一行一个字符串Q,表示对S的操作
如果第i个字母是小写字母c,表示第一种加字母c的操作
如果为-表示删除操作,保证所有删除操作前S都非空
|Q|<=10^5

Output

输出|Q|行,第i行表示i个操作之后S内有多少个不同子串

题目分析

陈老师神题x2,暂时只会做法一。

做法一:暴力回退

还是自己菜啊……这么个暴力都写了老久。

思路就是将每一次的修改值都记录下来,再在$undo()$里回退每一个修改。说得是轻松,但是具体的实现还是要仔细安排一下顺序和细节的。

其中$stk[top]$存修改的起止点;$last[]$存每次$extend()$前的$lst$值,用于路径回退上的$ch[x][c]$修改;$tag[]$表示点$i$这次操作新增点的数量,需要分为1,2讨论回退;$val[]$表示每次操作插入的字符$c$.

有2个新增点的$undo()$稍微复杂一点,这里$stk[]={从lst回退的p';fa[q];再次回退的p'';q}$.

 1 #include<bits/stdc++.h>
 2 const int maxn = 200035;
 3 
 4 int n,top,stk[maxn<<2],last[maxn<<2],tag[maxn<<2],val[maxn<<2];
 5 long long ans;
 6 struct SAM
 7 {
 8     int ch[maxn][27],fa[maxn],len[maxn],lst,tot;
 9     void init()
10     {
11         lst = tot = 1;
12     }
13     void extend(int c)
14     {
15         int p = lst, np = ++tot;
16         last[++last[0]] = lst;
17         lst = np, len[np] = len[p]+1;
18         val[tot] = c;
19         for (; p&&!ch[p][c]; p=fa[p]) ch[p][c] = np;
20         tag[tot] = 0, stk[++top] = p;
21         if (!p) fa[np] = 1; 
22         else{
23             int q = ch[p][c];
24             if (len[p]+1==len[q]) fa[np] = q;
25             else{
26                 int nq = ++tot;
27                 len[nq] = len[p]+1;
28                 tag[tot] = 2, val[tot] = c;
29                 memcpy(ch[nq], ch[q], sizeof ch[q]);  
30                 stk[++top] = fa[q];
31                 fa[nq] = fa[q], fa[q] = fa[np] = nq;
32                 for (; p&&ch[p][c]==q; p=fa[p]) ch[p][c] = nq;
33                 stk[++top] = p, stk[++top] = q;
34             }
35         }
36         ans += len[np]-len[fa[np]];
37         if (!tag[tot]) tag[tot] = 1;
38     }
39     void undo()
40     {
41         if (tag[tot]==1){
42             int p = last[last[0]--], fnd = stk[top--], c = val[tot];
43             ans -= len[tot]-len[fa[tot]], lst = p;
44             for (; p!=fnd; p=fa[p]) ch[p][c] = 0;
45             memset(ch[tot], 0, sizeof ch[tot]), --tot;
46         }else{
47             int q = stk[top--], fnd2 = stk[top--], fatq = stk[top--], fnd1 = stk[top--];
48             int p = last[last[0]--], c = val[tot];
49             ans -= len[tot-1]-len[fa[tot-1]], lst = p;
50             for (; p!=fnd1; p=fa[p]) ch[p][c] = 0;
51             fa[q] = fatq;
52             for (; p!=fnd2; p=fa[p]) ch[p][c] = q;
53             memset(ch[tot], 0, sizeof ch[tot]), --tot;
54             memset(ch[tot], 0, sizeof ch[tot]), --tot;
55         }
56     }
57 }f;
58 char s[maxn];
59 
60 int main()
61 {
62     f.init();
63     scanf("%s",s+1);
64     n = strlen(s+1);
65     for (int i=1; i<=n; i++)
66     {
67         if (s[i]=='-') f.undo();
68         else f.extend(s[i]-'a');
69         printf("%lld
",ans);
70     }
71     return 0;
72 }

做法二:后缀平衡树

具体的实现似乎可以用hash求LCP+multiset解决

END

原文地址:https://www.cnblogs.com/antiquality/p/10512652.html