您的位置:首页 > 其它

HDU 3518 Boring counting

2016-10-27 16:27 337 查看
题目:Boring counting

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

题意:给一个字符串,问有多少子串出现过两次以上,重叠不能算两次,比如ababa,aba只出现一次。

思路:

  网上搜的题解估计大部分都是后缀数组,但字典树+优化是可以解决该问题的。

  字典树解决这题难点就是内存,先不考虑内存,那么可以遍历起始点,然后添加入字典树,比如现在abab要添加进字典树,如果原本已经存在abab,并且两个不重叠,那么ans++,同时将abab标记掉,如果不存在,记录此时的下标以便等会判断是否重叠。(很简单的思路。)

  现在解决内存,可以计算,如果要通过内存限制,字典树节点只能27万左右。但如果只设置这么大,最后会超出,会RE(G++好像会显示TLE),可以想象,字典树上很多节点的next[26]都是-1,浪费空间,因此可以把next[26]换成vector,动态申请,查找时多花一点时间遍历,但内存大大减小。

---------------------------------------------------------------------------------

  下面是后缀数组解决该问题的方法:

  首先要明白后缀数组里几个数组的用法,这里不详述了。

  首先,我们可以遍历满足要求的字串的长度len,从1 到ls/2,然后遍历一遍height数组,height[i]表示排名第i 的后缀和排名第i-1 的后缀的最长公共前缀长度,那么如果height[i]>=len,这就有可能是答案了,只要不重叠就可以了,重叠可以用sa数组判断,可以找出最左边的下标记为l,最右边的下标记为r,只要l+len<=r就可以了,注意,height<len以后就是另外的字符串了。

AC代码:

1 #include<stdio.h>
2 #include<string.h>
3 #include<stdlib.h>
4 #include<math.h>
5 #include<set>
6 #include<map>
7 #include<list>
8 #include<stack>
9 #include<queue>
10 #include<vector>
11 #include<string>
12 #include<algorithm>
13 using namespace std;
14 #define lson rt<<1
15 #define rson rt<<1|1
16 #define N 1010
17 #define M 100010
18 #define Mod 1000000007
19 #define LL long long
20 #define INF 0x7fffffff
21 #define FOR(i,f_start,f_end) for(int i=f_start;i<=f_end;i++)
22 #define For(i,f_start,f_end) for(int i=f_start;i<f_end;i++)
23 #define REP(i,f_end,f_start) for(int i=f_end;i>=f_start;i--)
24 #define Rep(i,f_end,f_start) for(int i=f_end;i>f_start;i--)
25 #define MT(x,i) memset(x,i,sizeof(x))
26 #define gcd(x,y) __gcd(x,y)
27 const double PI = acos(-1);
28
29 char s1[1010];
30 int ws
,wv
;
31 int sa
,r
,wx
,wy
;
32 int height
;
33 bool cmp(int *r,int a,int b,int l)
34 {
35   return r[a]==r[b]&&r[a+l]==r[b+l];
36 }
37 void da(int *r,int n,int m)
38 {
39   int *x=wx,*y=wy;
40   for(int i=0;i<m;i++) ws[i]=0;
41   for(int i=0;i<n;i++) ws[x[i]=r[i]]++;
42   for(int i=1;i<m;i++) ws[i]+=ws[i-1];
43   for(int i=n-1;i>=0;i--) sa[--ws[x[i]]]=i;
44   int i,j,p,*t;
45   for(j=1,p=1;p<n;j*=2,m=p)
46   {
47     for(p=0,i=n-j;i<n;i++) y[p++]=i;
48     for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
49     for(i=0;i<n;i++) wv[i]=x[y[i]];
50     for(i=0;i<m;i++) ws[i]=0;
51     for(i=0;i<n;i++) ws[wv[i]]++;
52     for(i=1;i<m;i++) ws[i]+=ws[i-1];
53     for(i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i];
54     for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)
55       x[sa[i]]=cmp(y,sa[i],sa[i-1],j)?p-1:p++;
56   }
57   for(int i=0;i<n;i++)
58   {
59     r[sa[i]]=i;
60   }
61 }
62 void calHeight(int n)
63 {
64   int h=0;
65   for(int i=0;i<n;i++)
66   {
67     if(r[i]==0) h=0;
68     else
69     {
70       int k=sa[r[i]-1];
71       if(--h<0) h=0;
72       while(s1[k+h]==s1[i+h]) h++;
73     }
74     height[r[i]]=h;
75   }
76 }
77
78 int main()
79 {
80   while(~scanf("%s",s1))
81   {
82     if(s1[0]=='#') break;
83     int ls = strlen(s1);
84     for(int i=0;i<ls;i++)
85     {
86       r[i]=s1[i]-'a'+1;
87     }
88     r[ls++]=0;
89     da(r,ls,27);
90     calHeight(ls);
91     int ans = 0;
92     for(int i=1;i<=(ls-1)/2;i++)
93     {
94       int flag = 0;
95       int l=INF,r=-1;
96       for(int j=2;j<ls;j++)
97       {
98         if(height[j]>=i)
99         {
100           l = min(sa[j],min(sa[j-1],l));
101           r = max(sa[j],max(sa[j-1],r));
102           if(flag==0&&l+i<=r)
103           {
104             ans++;
105             flag=1;
106           }
107         }
108         else
109         {
110           flag=0;
111           l=INF;
112           r=-1;
113         }
114       }
115     }
116     printf("%d\n",ans);
117   }
118   return 0;
119 }


后缀数组
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: