zoukankan      html  css  js  c++  java
  • hdu4436-str2int(后缀数组 or 后缀自动机)

    题意:给你一堆字符串,仅包含数字'0'到'9'。

    例如 101 123

    有一个字符串集合S包含输入的N个字符串,和他们的全部字串。

    操作字符串很无聊,你决定把它们转化成数字。

    你可以把一个字符串转换成一个十进制整数。

    如果一个数字出现了多次,只留一个。

    计算所有数字的和,模2012。

    1<=N<=10000

    题解:参考 http://blog.csdn.net/kqzxcmh/article/details/8122747

    每个后缀的所有前缀就是所有子串。sa[i]和sa[i-1]有height[i]的长度是重复的,可以不考虑。

    把字符串连起来,中间加分隔符,构成一个大的字符串,然后整体求后缀数组。

    对于一个字符串123
    可以v[0]=1 v[1]=12 v[3]=123  v[i]是0-i构成的数字
    sum是前缀和 sum[0]=1 sum[1]=13 sum[2]=136

    对于后缀23 sum[2]-sum[0] 得到 12+123的和

    对于12需要减去10 对于123需要减去100

    所以求l~r 首先需要sum[r]-sum[l-1], 然后设x=r-l

    需要减去 for(int i=1;i<=x;++i) pow(10, i);

    可以预处理出来。

    太难辣!!像我这种辣鸡不适合这么难的题T^T

    时限3s,187ms还是比较快的。

    //后缀数组
    #include <stdio.h>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    
    const int N = int(2e5)+10;
    const int M = 2012;
    
    int cmp(int *r,int a,int b,int l){
        return (r[a]==r[b]) && (r[a+l]==r[b+l]);
    }
    // 用于比较第一关键字与第二关键字,
    // 比较特殊的地方是,预处理的时候,r[n]=0(小于前面出现过的字符)
    
    int wa[N],wb[N],wss[N],wv[N];
    int sa[N];          // 排第i的是谁 i:1~n sa:0~n-1
    int rk[N],          // i排第几 i:0~n-1 rk:1~n
        height[N];      // 排名相邻的两个后缀的最长公共前缀长度:suffix(sa[i-1])和(sa[i]) 的最长公共前缀,
    char str[N];
    
    void DA(char *r,int *sa,int n,int m){                    // 此处N比输入的N要多1,为人工添加的一个字符,用于避免CMP时越界 m是不同的字符的个数
        int i,j,p,*x=wa,*y=wb,*t;
        for(i=0;i<m;i++) wss[i]=0;
        for(i=0;i<n;i++) wss[x[i]=r[i]]++;
        for(i=1;i<m;i++) wss[i]+=wss[i-1];
        for(i=n-1;i>=0;i--) sa[--wss[x[i]]]=i;
        for(j=1,p=1;p<n;j*=2,m=p)
        {
            for(p=0,i=n-j;i<n;i++) y[p++]=i;
            for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
            for(i=0;i<n;i++) wv[i]=x[y[i]];
            for(i=0;i<m;i++) wss[i]=0;
            for(i=0;i<n;i++) wss[wv[i]]++;
            for(i=1;i<m;i++) wss[i]+=wss[i-1];
            for(i=n-1;i>=0;i--) sa[--wss[wv[i]]]=y[i];
            for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)
                x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
        }
    }
    
    void calheight(char *r,int *sa,int n){                    // 此处N为实际长度
        int i,j,k=0;
        for(i=1;i<=n;i++) rk[sa[i]]=i;
        for(i=0;i<n; height[rk[i++]] = k )
        for(k?k--:0,j=sa[rk[i]-1]; r[i+k]==r[j+k]; k++);
    }
    
    char word[N];
    int d[N];   // 该字符串的结束位置
    int sum[N];
    int v[N];
    int Pow[N];
    
    int cal(int l, int r) {
        if (l > r) return 0;
        //printf(">>%d %d %d %d %d
    ", l, r, sum[r+1], sum[l], v[l]);
        //printf("%d
    ", ((sum[r+1]-sum[l]) - v[l]*Pow[r-l+1]%M + M) % M);
        return ((sum[r+1]-sum[l]) - v[l]*Pow[r-l+1]%M + M) % M;
    }
    
    int main(int argc, char const *argv[])
    {
        //freopen("in.txt", "r", stdin);
        Pow[0]=1;Pow[1]=10;
        for (int i=2;i<N;++i)
            Pow[i]=(Pow[i-1]+1)*10%M;
        int n;
        while (~scanf("%d", &n)) {
            int idx = 0;
            for (int i = 0; i < n; ++i) {
                scanf("%s", word);
                int len = strlen(word);
                int val = 0;
                for (int j = 0; j < len; ++j) {
                    str[idx] = word[j] - '0' + 1;
                    val = (val * 10 + str[idx] - 1) % M;
                    v[idx+1] = val; //+1是因为会有-1 <0很麻烦= =
                    sum[idx+1] =  (sum[idx] + val) % M;
                    d[idx] = len - j + idx;
                    idx++;
                }
                d[idx] = idx;
                sum[idx+1] = v[idx+1] = 0;
                str[idx++] = 11; //1~11
            }
            str[idx] = 0;
            DA(str, sa, idx+1, 15);
            calheight(str, sa, idx);
            int ans = 0;
            for (int i = 0; i < idx; ++i) {
                if (str[i] == 1 || str[i] == 11) continue;  // 不要前导零
                int l = i + height[rk[i]];//重复的部分
                int r = d[i]-1;
                //printf("i=%d,l=%d, r=%d
    ", i, l, r);
                if (l > r) continue;
                ans = (ans + cal(i, r) - cal(i, l-1)) % M;
                //ans = (ans + cal(l, r)) % M; 这个不对 因为所求字符串是从i开始的
                ans = (ans + M) % M;
            }
            printf("%d
    ", ans);
        }
        return 0;
    }

    后缀自动机= = 先留坑吧orz。。。。

  • 相关阅读:
    C语言第五次作业
    C语言第四次作业
    C语言第三次作业

    第一次作业
    C语言第五次作业
    C语言第四次作业
    C语言第三次作业
    C语言理论作业—2
    燕勇鹏-160809318
  • 原文地址:https://www.cnblogs.com/wenruo/p/5821014.html
Copyright © 2011-2022 走看看