hdu 3518 Boring counting (后缀自动机)

http://acm.hdu.edu.cn/showproblem.php?pid=3518

题意:

给一个字符串,问字符串中,至少出现2次且不相交的本质不同的子串有多少个。本质不同给的子串是说存在至少一位的字母不同。

思路:

考虑SAM

SAM上的一个节点表示的是一段长度从[st[st[i].link],len+1,st[i].len]的字符串。

考虑其right集合,如果right集合中最大的r设为rightmost,最小的r设为leftmost.

那么如果rightmost-leftmost+1 > st[i].len ,说明什么呢?

说明该状态所表示的最长的字符串能够至少放下两个而不重叠。

即从[leftmost-st[i].len+1,leftmost]一段 和 从[rightmost-st[i].len+1,rightmost]一段。

如果最长的能放下,那么其他的也一定能放下。因此对答案贡献为st[i].len - st[st[i].link].len

如果rightmost-leftmost+1<=st[i].len,也就是rightmost-leftmost<st[i].len

这个时候长的字符串因为重叠了不能出现2次,但是短的字符串仍然可以贡献答案。

考虑如下图:

此时对答案的贡献为rightmost-leftmost+1 - (st[st[i].link].len+1),化简得到rightmost-leftmost-st[st[i].link].len

综合2种情况,SAM中每个节点对答案的贡献是** min(rightmost-leftmost,st[i].len)-st[st[i].link].len**

需要注意的是只有在st[i].link存在并且rightmost-leftmost>st[st[i].link].len 时 才更新答案

leftmost可以在构建的时候直接求出,rightmost用拓扑序更新下即可。

  1/* ***********************************************
  2Author :111qqz
  3Created Time :2017年11月08日 星期三 18时50分18秒
  4File Name :4436.cpp
  5************************************************ */
  6
  7#include <bits/stdc++.h>
  8#define PB push_back
  9#define fst first
 10#define sec second
 11#define lson l,m,rt<<1
 12#define rson m+1,r,rt<<1|1
 13#define ms(a,x) memset(a,x,sizeof(a))
 14typedef long long LL;
 15#define pi pair < int ,int >
 16#define MP make_pair
 17
 18using namespace std;
 19const double eps = 1E-8;
 20const int dx4[4]={1,0,0,-1};
 21const int dy4[4]={0,-1,1,0};
 22const int inf = 0x3f3f3f3f;
 23#define MAXALP 30
 24const int mod = 2012;
 25struct state
 26{
 27    int len, link, nxt[MAXALP];
 28    int leftmost; //由于要求出现位置最小的,所以维护某个状态的right集合中r值最小的
 29    int rightmost;
 30};
 31const int N =1E3+7;
 32state st[N*2];
 33int sz, last,rt;
 34char s[N];
 35int cnt[2*N],rk[2*N];//for radix sort
 36void sa_init()
 37{
 38    sz = 0;
 39    last = rt = ++sz;
 40    st[1].len = 0;
 41    st[1].link=-1;
 42    st[1].rightmost=0;
 43    ms(st[1].nxt,-1);
 44}
 45void sa_extend(int c,int head)
 46{
 47    int cur = ++sz;
 48    st[cur].len = st[last].len + 1;
 49    st[cur].leftmost = st[cur].rightmost = head;
 50    memset(st[cur].nxt, -1, sizeof(st[cur].nxt));
 51
 52    int p;
 53    for (p = last; p != -1 && st[p].nxt[c] == -1; p = st[p].link)
 54        st[p].nxt[c] = cur;
 55    if (p == -1) {
 56        st[cur].link = rt;
 57    } else {
 58        int q = st[p].nxt[c];
 59        if (st[p].len + 1 == st[q].len) {
 60
 61            st[cur].link = q;
 62        } else {
 63            int clone = ++sz ;
 64            st[clone].len = st[p].len + 1;
 65            st[clone].link = st[q].link;
 66            memcpy(st[clone].nxt, st[q].nxt, sizeof(st[q].nxt));
 67            st[clone].leftmost = st[q].leftmost;
 68            st[clone].rightmost = st[q].rightmost;
 69            for (; p != -1 && st[p].nxt[c] == q; p = st[p].link)
 70                st[p].nxt[c] = clone;
 71            st[q].link = st[cur].link = clone;
 72        }
 73    }
 74    last = cur;
 75}
 76void topo()
 77{
 78    ms(cnt,0); 
 79    for (int i = 1 ; i <= sz ; i++) cnt[st[i].len]++;
 80    for ( int i = 1 ; i <= sz ; i++) cnt[i]+=cnt[i-1];
 81    for (int i = 1 ; i <= sz  ;i++) rk[cnt[st[i].len]--] = i;
 82}
 83char S[N];
 84int main()
 85{
 86#ifndef  ONLINE_JUDGE 
 87        freopen("./in.txt","r",stdin);
 88#endif
 89
 90    while (~scanf("%s",S))
 91    {
 92        if (S[0]=='#') break;
 93        sa_init();
 94        for ( int i = 0,len = strlen(S) ; i < len ; i++)
 95        {
 96        sa_extend(S[i]-'a',i);
 97        }
 98
 99        topo();
100        for ( int i = sz ; i >=1 ; i--)
101        {
102        int v = rk[i];
103        int fa = st[v].link;
104        st[fa].rightmost = max(st[fa].rightmost,st[v].rightmost);
105//      printf("len:%d rightmost %d leftmost:%d\n",st[i].len,st[i].rightmost,st[i].leftmost);
106        } 
107
108        LL ans = 0 ;
109        for ( int i = 1 ; i <= sz ; i++) if (st[i].link!=-1&&st[i].rightmost-st[i].leftmost>st[st[i].link].len)
110            ans += min(st[i].len,st[i].rightmost-st[i].leftmost)-st[st[i].link].len;
111        printf("%lld\n",ans);
112    }
113
114
115
116#ifndef ONLINE_JUDGE  
117    fclose(stdin);
118#endif
119    return 0;
120}
121
122
123
124
125
126
127
128/* ***********************************************
129Author :111qqz
130Created Time :2017年11月15日 星期三 19时06分15秒
131File Name :SAM.cpp
132************************************************ */
133
134#include <bits/stdc++.h>
135#define PB push_back
136#define fst first
137#define sec second
138#define lson l,m,rt<<1
139#define rson m+1,r,rt<<1|1
140#define ms(a,x) memset(a,x,sizeof(a))
141typedef long long LL;
142#define pi pair < int ,int >
143#define MP make_pair
144
145using namespace std;
146const double eps = 1E-8;
147const int dx4[4]={1,0,0,-1};
148const int dy4[4]={0,-1,1,0};
149const int inf = 0x3f3f3f3f;
150#define MAXALP 30
151const int N=1E3+7;
152struct SAM
153{
154    struct state
155    {
156    int len, link, nxt[MAXALP];
157    int leftmost; //某个状态的right集合中r值最小的
158    int rightmost;//某个状态的right集合的r的最大值
159    int Right; //right集合大小
160    };
161    state st[N*2];
162    char S[N];
163    int sz, last,rt;
164    char s[N];
165    int cnt[2*N],rk[2*N];//for radix sort
166    void init()
167    {
168    sz = 0;
169    last = rt = ++sz;
170    st[1].len = 0;
171    st[1].link=-1;
172    st[1].rightmost=0;
173    ms(st[1].nxt,-1);
174    }
175    void extend(int c,int head)
176    {
177    int cur = ++sz;
178    st[cur].len = st[last].len + 1;
179    st[cur].leftmost = st[cur].rightmost = head;
180    memset(st[cur].nxt, -1, sizeof(st[cur].nxt));
181    int p;
182    for (p = last; p != -1 && st[p].nxt[c] == -1; p = st[p].link)
183        st[p].nxt[c] = cur;
184    if (p == -1) {
185        st[cur].link = rt;
186    } else {
187        int q = st[p].nxt[c];
188        if (st[p].len + 1 == st[q].len) {
189
190        st[cur].link = q;
191        } else {
192        int clone = ++sz ;
193        st[clone].len = st[p].len + 1;
194        st[clone].link = st[q].link;
195        memcpy(st[clone].nxt, st[q].nxt, sizeof(st[q].nxt));
196        st[clone].leftmost = st[q].leftmost;
197        st[clone].rightmost = st[q].rightmost;
198        for (; p != -1 && st[p].nxt[c] == q; p = st[p].link)
199            st[p].nxt[c] = clone;
200        st[q].link = st[cur].link = clone;
201        }
202    }
203    last = cur;
204    }
205    void topo()
206    {
207    ms(cnt,0); 
208    for (int i = 1 ; i <= sz ; i++) cnt[st[i].len]++;
209    for ( int i = 1 ; i <= sz ; i++) cnt[i]+=cnt[i-1];
210    for (int i = 1 ; i <= sz  ;i++) rk[cnt[st[i].len]--] = i;
211    }
212    void pre()  //跑拓扑序,预处理一些东西
213    {
214    for ( int i = sz ; i >= 2 ; i--)
215    {
216        int v = rk[i];
217        int fa = st[v].link;
218        if (fa==-1) continue;
219        st[fa].rightmost = max(st[fa].rightmost,st[v].rightmost);
220        st[fa].Right += st[v].Right;
221    }
222    }
223    void solve()
224    {
225
226    LL ans = 0 ;
227    for ( int i = 1 ; i <= sz ; i++) if (st[i].link!=-1&&st[i].rightmost-st[i].leftmost>st[st[i].link].len)
228        ans += min(st[i].len,st[i].rightmost-st[i].leftmost)-st[st[i].link].len;
229    printf("%lld\n",ans);
230    }
231}A;
232int main()
233{
234#ifndef  ONLINE_JUDGE 
235    freopen("./in.txt","r",stdin);
236#endif
237
238    while (~scanf("%s",A.S))
239    {
240    if (A.S[0]=='#') break;
241    A.init();
242    for ( int i = 0,len = strlen(A.S) ; i < len ; i++)
243        {
244        A.extend(A.S[i]-'a',i);
245        }
246        A.topo();
247    A.pre();
248    A.solve();
249    }
250
251
252
253#ifndef ONLINE_JUDGE  
254    fclose(stdin);
255#endif
256    return 0;
257}