題目連結: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 310000int 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;}