题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4416
这个题目我感觉不好写,各种出错,最后参考这篇博客的程序和思想才得以完成:http://hi.baidu.com/arosliu/item/ec421c23bf862ef951fd8769
开始按照以前的后缀数组的写法,老是Run time error 后来改成上面链接的后缀数组写法就没问题了,目前还不知道问题出在什么地方
这个题目的思想我觉得非常巧妙,很好的利用了后缀数组和height数组的性质
首先处理这种多个字符串问题一般要是用后缀数组来处理的话就是字符串拼接,拼接的同时在每个串之间插入一个独一无二的字符
所以这里在处理的时候把字符全部转换成整形数,原因是如果用字符,哪来那么多独一无二的字符呢
然后拼接完成之后求sa rank 和height,然后求出这个数组的所有不用子串的数目,这个数目还是比较好求的
求完之后减去子串中包含特殊字符(我们拼接时候添加的)的子串数目,现在得出的结果就是不包含特殊字符的不同子串的个数
实际上就所有串中不同子串的数目(这里好好理解下),因为前面加后面的总共是len_ab,后面的是len_b,结果肯定是相减嘛
同样的做法求出后面,也就是不包含第一个串的这个值,然后两个相减就得到了结果!
至于怎样求解包含特殊字符的不同字串个数呢,其实就是下面这段代码
for (i = 0; i < number; i++)
{
l -= L[i];
len_ab-= (L[i]+1)*l;
l--;
}
L[i]表示第i个字符串的长度,L初始值等于len-1;
每次都这样减去,为什么这样是正确的,因为包含了这个特殊字符,那么不同的子串个数就是个组合,前面取连续的不同的L[i]+1个,后面连续的
不同的l个,两者相乘,当然这个只是包含第i个特殊字符的情况,随着i++的进行,结果就出来了!
/*4416*/
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <math.h>
#include <string.h>
using namespace std;
#define maxn 310000
int top[maxn],tmp[maxn];
int len;
char rec[maxn];
int rec1[maxn];
int L[maxn];
int sa[maxn];
int rank[maxn],height[maxn];
int *str;
void makesa()
{
int lena = len < 256 ? 256 : len;
memset(top, 0, lena*sizeof(int));
for (int i = 0; i < len; i++)
top[rank[i] = str[i]&(-1)]++;
for (int i = 1; i < lena; i++)
top[i] += top[i-1];
for (int i = 0; i < len ; i++)
sa[--top[rank[i]]] = i;
for (int k = 1; k < len; k <<= 1)
{
for (int i = 0; i < len; i++)
{
int j = sa[i]-k;
if (j < 0)
j += len;
tmp[top[rank[j]]++] = j;
}
int j = sa[tmp[0]] = top[0] = 0;
for (int i = 1; i < len; i++)
{
if (rank[tmp[i]] != rank[tmp[i-1]] || rank[tmp[i]+k] != rank[tmp[i-1]+k])
top[++j] = i;
sa[tmp[i]] = j;
}
memcpy(rank, sa , len*sizeof(int));
memcpy(sa , tmp, len*sizeof(int));
if (j+1 >= len)
break;
}
}
void lcp()
{
height[0] = 0;
for (int i = 0, k = 0, j = rank[0]; i+1 < len; i++, k++)
while (k >= 0 && str[i] != str[sa[j-1]+k])
{
height[j] = k--;
j = rank[sa[j]+1];
}
}
int main()
{
int i,j,k=0;
int left,right;
int mid,number,ans,ans1,len_f;
long long len_ab,len_b,l;
int t;
scanf("%d",&t);
while(t--)
{
len_ab=len_b=0;
scanf("%d",&number);
scanf("%s",rec);
for(len=0;rec[len];len++)
{
rec1[len]=rec[len]-'a'+1;
}
L[0]=len;
for(i=1;i<=number;i++)
{
rec1[len++]=26+i;
scanf("%s",rec);
for(j=0;rec[j];j++)
rec1[len++]=rec[j]-'a'+1;
L[i]=j;
}
rec1[len++]=0;
str=rec1;
makesa();
lcp();
for ( i = 1; i < len; i++)
len_ab += len-1-sa[i]-height[i];
l=len-1;
for (i = 0; i < number; i++)
{
l -= L[i];
len_ab-= (L[i]+1)*l;
l--;
}
len-=L[0]+1;
str=rec1+L[0]+1;
makesa();
lcp();
for(i = 1; i < len; i++)
len_b += len-1-sa[i]-height[i];
l=len-1;
for (i = 1; i < number; i++)
{
l -= L[i];
len_b-= (L[i]+1)*l;
l--;
}
cout<<"Case "<<++k<<": "<<len_ab-len_b<<endl;
}
return 0;
}