hdu 4622 | 2013 Multi-University Training Contest 3 Reincarnation (后缀自动机)

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

题意:

给一个字符串,给出若干询问,每组询问给一个区间[l,r],问区间中本质不同的字符串的个数。

思路:

观察发现,有10000组查询,字符串的长度最多才2000,所以可以预处理一波。

我们先考虑如何处理整个区间中,本质不同的子串数量。

考虑SAM,由于后缀自动机中每一条从初始状态出发的路径都对应的一个子串,同时后缀自动机是最简的,所以问题也就变成了从初始状态开始不同路径的数量

每个节点 u 表示的子串长度在 [min[u],max[u]]范围内.

又由于max(u.fail) = min(u)-1

因此u表示的子串的长度就是变成了**(max[u.fail],max[u] ]  (注意区间,是左闭右开)**

由于每个长度的串都出现了一次,因此这个状态子串的个数就是max[u] - max[u.fail]

如果是统计整个串的本质不同的串的个数,那么buildSAM之后统计一下就行了。

现在是询问区间。

问题变成了,从[l,r]到[l,r+1],答案的改变是什么。

在SAM上添加一个字符之后,SAM当前的状态变成了cur,那么实际上,对答案的贡献,就是初始状态到新的状态cur的不同路径数目。

也就是max[cur]-max[cur.fail]

  1#include <bits/stdc++.h>
  2#define PB push_back
  3#define fst first
  4#define sec second
  5#define lnxt l,m,rt<<1
  6#define rnxt m+1,r,rt<<1|1
  7#define ms(a,x) memset(a,x,sizeof(a))
  8typedef long long LL;
  9#define pi pair < int ,int >
 10#define MP make_pair
 11
 12using namespace std;
 13const double eps = 1E-8;
 14const int dx4[4]={1,0,0,-1};
 15const int dy4[4]={0,-1,1,0};
 16const int inf = 0x3f3f3f3f;
 17const int N=2017;
 18const int maxn = 4017;
 19LL ret;
 20struct node{
 21    node*nxt[26],*fail;
 22    LL len,cnt;
 23    void init()
 24    {
 25    for ( int i = 0 ; i < 26 ; i++) nxt[i]=NULL;
 26    fail=NULL;
 27    len=cnt=0;
 28    }
 29};
 30struct SAM{
 31    node no[maxn];
 32    node*root;
 33    int cnt;
 34     node*  newnode(){
 35    ms(no[cnt].nxt,0);
 36    no[cnt].fail=NULL;
 37    no[cnt].len=no[cnt].cnt=0;
 38    return &no[cnt++];
 39    }
 40    SAM(){
 41    cnt = 0;
 42    root = newnode();
 43    }
 44    void init()
 45    {
 46    cnt = 0;
 47    root =newnode();
 48    no[0].init();
 49    }
 50    node*add(int c,node*p){
 51        node*cur = newnode();
 52        cur->len = p->len+1;
 53    node *lst = cur;
 54        while(p&&!p->nxt[c]){
 55            p->nxt[c] = cur;
 56            p = p->fail;
 57        }
 58        if(!p){
 59            cur->fail = root;
 60            return cur;
 61        }
 62        node *q = p->nxt[c];
 63        if(p->len+1==q->len){
 64            cur->fail = q;
 65        }else{
 66            node*nq = newnode();
 67            *nq = *q;
 68            q->fail = cur->fail = nq;
 69            nq->len = p->len+1;
 70            while(p&&p->nxt[c]==q){
 71                p->nxt[c] = nq;
 72                p = p->fail;
 73            }
 74        }
 75        return cur;
 76    }
 77    LL calc(node *cur)
 78    {
 79    return cur->len - cur->fail->len;
 80    }
 81};
 82SAM sam;
 83string A;
 84LL ans[N][N];
 85int main()
 86{
 87    #ifndef  ONLINE_JUDGE 
 88    freopen("./in.txt","r",stdin);
 89  #endif
 90    int T;
 91    cin>>T;
 92    while (T--)
 93    {
 94        cin>>A;
 95        int q;
 96        ms(ans,0);
 97        int len = A.length();
 98        //预处理一下,就只需要2000个SAM了
 99        for ( int i = 0 ; i < len ; i++)
100        {
101        sam.init();
102        node *cur = sam.root;
103        for ( int j = i ; j < len ; j++)
104        {
105            cur = sam.add(A[j]-'a',cur);
106            ans[i+1][j+1] = ans[i+1][j] + sam.calc(cur);
107
108        }
109        }
110        scanf("%d",&q);
111        while (q--)
112        {
113        int l,r;
114        scanf("%d %d",&l,&r);
115        printf("%lld\n",ans[l][r]);
116        }
117    }
118
119  #ifndef ONLINE_JUDGE  
120  fclose(stdin);
121  #endif
122    return 0;
123}