hdu 4436 | 2012 Asia Tianjin Regional Contest str2int (dp+后缀自动机,多串建立)

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

题意:

给出n个仅由数字组成的字符串,问n个字符串的所有不同子串的和。

思路:

SAM水题

从初始状态开始,走过所有路径,就是该SAM表示的所有的(不重复)子串。

因此只需要从根节点按照拓扑序(这回是根据len从小到大)转移好了。

考虑num[i]表示从SAM中的init状态到i状态能表示的子串的数量。

dp[i]表示从SAM中的init状态到i状态所表示的子串对应的和(也就是到该节点的子串的答案)

那么对于SAM中一个u->v(其中u和v是状态,设转移边为j,j属于0..9)的转移

num[v]+=num[u]

dp[v] = dp[u] * 10 + num[u] * j

初始根节点num[rt]=1,表示唯一的空串。

需要注意,前缀0的数不考虑,所以SAM中 init状态不转移0这条边。

  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 12
 24const int mod = 2012;
 25struct state
 26{
 27    int len, link, nxt[MAXALP];
 28};
 29const int N =1E5+7;
 30state st[N*2];
 31int sz, last,rt;
 32char s[N];
 33int cnt[2*N],rk[2*N];//for radix sort
 34void sa_init()
 35{
 36    sz = 0;
 37    last = rt = ++sz;
 38    st[1].len = 0;
 39    st[1].link=-1;
 40    ms(st[1].nxt,-1);
 41    ms(cnt,0);
 42}
 43void sa_extend(int c)
 44{
 45    int cur = ++sz;
 46    st[cur].len = st[last].len + 1;
 47    memset(st[cur].nxt, -1, sizeof(st[cur].nxt));
 48    int p;
 49    for (p = last; p != -1 && st[p].nxt[c] == -1; p = st[p].link)
 50        st[p].nxt[c] = cur;
 51    if (p == -1) {
 52        st[cur].link = rt;
 53    } else {
 54        int q = st[p].nxt[c];
 55        if (st[p].len + 1 == st[q].len) {
 56            st[cur].link = q;
 57        } else {
 58            int clone = ++sz ;
 59            st[clone].len = st[p].len + 1;
 60            st[clone].link = st[q].link;
 61            memcpy(st[clone].nxt, st[q].nxt, sizeof(st[q].nxt));
 62            for (; p != -1 && st[p].nxt[c] == q; p = st[p].link)
 63                st[p].nxt[c] = clone;
 64            st[q].link = st[cur].link = clone;
 65        }
 66    }
 67    last = cur;
 68}
 69int num[2*N],dp[2*N]; //num[i]表示SAM上到达状态i的子串的数量。
 70//sum[i]表示SAM上到达状态i的答案。
 71int n;
 72int main()
 73{
 74#ifndef  ONLINE_JUDGE 
 75        freopen("./in.txt","r",stdin);
 76#endif
 77
 78    while (~scanf("%d",&n))
 79    {
 80    sa_init();
 81
 82    ms(num,0);
 83    ms(dp,0);
 84    for ( int i = 1 ; i <= n ; i++)
 85    {
 86        scanf("%s",s+1);
 87        int len_s = strlen(s+1);
 88        for ( int i = 1 ; i <= len_s ; i++)
 89        {
 90        sa_extend(s[i]-'0');
 91        }
 92        sa_extend(10); //插入一个没有出现的字符做分隔符
 93    }
 94    //  a simple radix sort
 95    for (int i = 1 ; i <= sz ; i++) cnt[st[i].len]++;
 96    for ( int i = 1 ; i <= sz ; i++) cnt[i]+=cnt[i-1];
 97    for (int i = 1 ; i <= sz  ;i++) rk[cnt[st[i].len]--] = i;
 98
 99
100    num[rt] = 1;
101    for ( int i = 1 ; i <= sz ; i++)
102    {
103        int u = rk[i];
104//      printf("u:%d\n",u);
105        for ( int j = 0 ;j < 10 ; j++)
106        {
107        if (i==1&&j==0) continue;  //有前缀0不更新答案,也就是初始态的不转移到0. i==1时表示初始态
108        int v = st[u].nxt[j];
109        if (v==-1) continue;
110        num[v] += num[u];
111        dp[v] = dp[v] + (dp[u] * 10 ) + num[u] * j; //类似数位dp,u所表示的状态左移一位,所以dp[u]*10,
112        //然后有num[u]个子串到达u,每个都可以转移j这条边,因此答案贡献是num[u]*j
113        dp[v] %=mod;
114        }
115    }
116    int ans = 0 ;
117    for ( int i = 1 ; i <= sz ; i++) ans = ( ans + dp[i])%mod;
118    printf("%d\n",ans);
119    }
120
121
122#ifndef ONLINE_JUDGE  
123    fclose(stdin);
124#endif
125    return 0;
126}