题意:给出n个字符串,求出所有字符串中出现的不同的整数和mod2012的值(即出现多次算一次)
思路:用后缀数组去重的方法很好想到,但是难点在于如何去快速的计算这些不同的子串。
比如两个串
123,124
他们的所有子串
原串:123#124
123#124
124
23#124
24
3#124
4
#124
而答案应是 1+12+123+124+2+23+24+3+4
那么我们该如何计算呢。
假设我们求的字符串 "12345"中的34,我们要怎么快速的求出。
我们定义了两个数组,一个是sum[]表示前缀和,一个是num[]表示数字,比如上面的例子
sum[0] = 1 num[0] = 1
sum[1] = 1 + 12 num[1] = 12
sum[2] = 1 + 12 + 123 num[2] = 123
... .....
sum[4] = 1 + 12 + 123 + 1234 + 12345 num[4] = 12345
我们首先将 sum[3] - sum[1] 得到 123 + 1234 , 而我们想要得到是 3 + 34
所以我们要减去 120 和 1200 即 num[1] * 110
用这样的方法就可以快速的求和
具体到我的样例
(1) 没重复 1+12+123=(sum[2]-sum[-1]-(num[-1]*ten[2--1]))-(sum[-1]-sum[-1]-(num[-1]*ten[-1--1]))
(2) 重复2个 124=1+12+124-1-12=(sum[6]-sum[3]-(num[3]*ten[6-3]))-(sum[5]-sum[3]-(num[3]*ten[5-3]))
(3)没重复 2+23=(sum[2]-sum[0]-(num[0]*ten[2-0]))-(sum[0]-sum[0]-(num[0]*ten[0-0]))
(4)重复1个 24=2+24-2=(sum[6]-sum[4]-(num[4]*ten[6-4]))-(sum[5]-sum[4]-(num[4]*ten[5-4]))
依次类推 记得去模就OK了
这里注意下数组下标会是-1 所以可以特判一下 不过其实也没啥 下标是-1的位置值为0
然后还有一个就是注意下 如果当前的前缀开头不是1~9 那么就跳过不计算
代码:
#include"cstdlib"
#include"cstdio"
#include"cstring"
#include"cmath"
#include"queue"
#include"algorithm"
#include"iostream"
using namespace std;
#define N 120123
int wa[N],wb[N],wv[N],wws[N];
int v[N],ra[N],sa[N],height[N],ten[N];
int num[N],sum[N],length[N];
char fuck[N];
int M=2012;
int cmp(int *r,int a,int b,int l)
{
return r[a]==r[b]&&r[a+l]==r[b+l];
}
void da(int n,int m)
{
int i,j,p,*x=wa,*y=wb;
for(i=0; i<m; i++) wws[i]=0;
for(i=0; i<n; i++) wws[x[i]=v[i]]++;
for(i=1; i<m; i++) wws[i]+=wws[i-1];
for(i=n-1; i>=0; i--) sa[--wws[x[i]]]=i;
for(j=1,p=1; p<n; j*=2,m=p)
{
for(i=n-j,p=0; 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++) wws[i]=0;
for(i=0; i<n; i++) wws[wv[i]]++;
for(i=1; i<m; i++) wws[i]+=wws[i-1];
for(i=n-1; i>=0; i--) sa[--wws[wv[i]]]=y[i];
for(swap(x,y),i=1,p=1,x[sa[0]]=0; i<n; i++) x[sa[i]]=cmp(y,sa[i],sa[i-1],j)?p-1:p++;
}
return ;
}
void gethei(int n)
{
int i,j,k=0;
for(i=1; i<=n; i++) ra[sa[i]]=i;
for(i=0; i<n; i++)
{
if(k) k--;
j=sa[ra[i]-1];
while(v[i+k]==v[j+k]) k++;
height[ra[i]]=k;
}
return ;
}
int main()
{
int n;
ten[0]=0;
for(int i=1;i<=N;i++) ten[i]=((ten[i-1]+1)*10)%M; //构造0,10,110....数组
while(scanf("%d",&n)!=-1)
{
int len=0;
int i,j;
for(i=0; i<n; i++)
{
scanf("%s",fuck);
int l=strlen(fuck);
int x=0,t=0;
for(j=0; j<l; j++,len++)
{
v[len]=fuck[j];
length[len]=l-j; //标记每段目标位置在哪
t=t*10+fuck[j]-'0'; //累加求和
t%=M;
x+=t;
x%=M;
sum[len]=x%M;
num[len]=t%M;
}
sum[len]=num[len]=0; //分隔符的地方置零
v[len++]='9'+i+1; //添加分隔符
}
sum[len]=num[len]=0;
v[len]=0;
da(len+1,12000);
gethei(len);
int ans=0;
for(i=1; i<=len; i++) //sa是从1开始的 1~len
{
if(v[sa[i]]>='1'&&v[sa[i]]<='9') //是不是1~9
{
int s=sa[i]-1,t1=s+length[sa[i]],t2=s+height[i]; //s为原始位置 t1为目标位置 t2为重复位置如题解所说
int sum1,sum2;
if(s<0) //特判
{
sum1=sum[t1];
sum2=sum[t2];
}
sum1=sum[t1]-sum[s]-num[s]*ten[t1-s];
sum2=sum[t2]-sum[s]-num[s]*ten[t2-s];
ans+=sum1-sum2;
ans=(ans%M+M)%M; //防止负数
}
}
printf("%d\n",ans%M);
}
return 0;
}