hdu 4029
题意:给你一个字符矩阵,统计不同的子矩阵的个数;
分析:枚举子矩阵的宽度w,对于每一个w,将每一行长度可以是w的字符串HASH成一个值,然后用map标记,因为宽确定了,hash完之后,然后如果相等就表示此时长度为w的字串
相等,将他们按照每一列排序形成一个字符串,此时如果莫两个长度为x的子串相等就表示此时x*w的子矩阵相同,
这样就是统计m个字符串的不同的子串的个数,这个是SA的论文题;n^3*logn
HASH会冲突,SEED选择很重要,当然也可以用2个SEED,这样冲突的概率就很小很小了;
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 #include<cmath> 6 #include<cstdlib> 7 #include<map> 8 #define mk make_pair 9 10 using namespace std; 11 typedef unsigned long long ULL; 12 typedef pair<ULL,ULL> pUU; 13 const int N = 50000+10; 14 const int M = 200+10; 15 const int SEED[2]= {13,11}; 16 char s[M][M]; 17 ULL hash[2][M][M],X[2][M]; 18 int n,m; 19 map<pUU,int> mp; 20 int str[N]; 21 struct Suffix_Array { 22 int a1[N],a2[N],c[N],sa[N],SA[N],*x,*y,n,m; 23 int height[N],*rank; 24 void sort(){ 25 for (int i = 0; i < m; i++) c[i] = 0; 26 for (int i = 0; i < n; i++) c[ x[i] ] ++; 27 for (int i = 0; i < m; i++) c[i+1] += c[i]; 28 for (int i = n-1; i >= 0; i--) SA[ --c[x[sa[i]]] ] = sa[i]; 29 } 30 void build_SA(int s[],int _n,int _m) { 31 n = _n; m = _m; 32 x = a1; y = a2; x[n] = y[n] = -1; 33 for (int i = 0; i < n; i++) x[i] = s[i], sa[i] = i; 34 sort(); 35 for (int k = 1; k <= n; k <<= 1) { 36 int p = 0; 37 for (int i = n-k; i < n; i++) sa[ p++ ] = i; 38 for (int i = 0; i < n; i++) if (SA[i] >= k) sa[ p++ ] = SA[i] - k; 39 sort(); 40 p = 0; y[SA[0]] = 0; 41 for (int i = 1; i < n; i++) { 42 if ( x[SA[i-1]] != x[SA[i]] || x[SA[i-1]+k] != x[SA[i]+k] ) p++; 43 y[SA[i]] = p; 44 } 45 swap(x,y); 46 if (p+1 == n) break; 47 m = p + 1; 48 } 49 rank = x; getHeight(s); 50 } 51 void getHeight(int s[]){ 52 int k = 0; 53 for (int i = 0; i < n; i++) { 54 if (k) k--; 55 if (rank[i] == 0) continue; 56 int j = SA[rank[i] - 1]; 57 while ( s[j+k] && s[i+k] == s[j+k]) k++; 58 height[ rank[i] ] = k; 59 } 60 height[n] = 0; 61 } 62 void check(){ 63 for (int i = 0; i < n; i++) cout<<SA[i]<<" "; cout<<endl; 64 for (int i = 0; i < n; i++) cout<<rank[i]<<" "; cout<<endl; 65 for (int i = 0; i < n; i++) cout<<height[i]<<" "; cout<<endl; 66 } 67 }H; 68 69 void init(){ 70 // 会用到的一定要清空; 71 for (int k = 0; k < 2; k++) 72 for (int i = 0; i < n; i++) { 73 hash[k][i][m] = 0;//会用到,如果不清空会wa; 74 hash[k][i][m-1] = s[i][m-1] - 'A'; 75 for (int j = m-2; j >= 0; j--) { 76 hash[k][i][j] = hash[k][i][j+1] * SEED[k] + s[i][j] - 'A'; 77 } 78 } 79 X[0][0] = X[1][0] = 1; 80 for (int k = 0; k < 2; k++) 81 for (int i = 1; i <= m; i++) { 82 X[k][i] = X[k][i-1] * SEED[k]; 83 } 84 } 85 86 int idx[N]; 87 int find(int n){ 88 int ret = idx[H.SA[0]]; 89 for (int i = 1; i < n; i++){ 90 ret += idx[H.SA[i]] - H.height[i]; 91 } 92 return ret; 93 } 94 void solve(){ 95 int ret = 0; 96 int cnt, tot; 97 for (int w = 1; w <= m; w++) { 98 cnt = 0, tot = 1; 99 mp.clear(); 100 for (int j = 0; j+w-1 < m; j++){ 101 for (int i = 0; i < n; i++) { 102 ULL tmp1 = hash[0][i][j] - hash[0][i][j+w] * X[0][w]; 103 ULL tmp2 = hash[1][i][j] - hash[1][i][j+w] * X[1][w]; 104 105 if (mp.find(mk(tmp1,tmp2)) == mp.end()){ 106 mp[mk(tmp1,tmp2)] = tot++; 107 } 108 idx[cnt] = n - i; 109 str[cnt++] = mp[mk(tmp1,tmp2)]; 110 } 111 idx[cnt] = 0; 112 str[cnt++] = tot++; 113 } 114 H.build_SA(str,cnt,tot); 115 // H.check(); 116 ret += find(cnt); 117 } 118 printf("%d\n",ret); 119 120 } 121 int main(){ 122 int T, cas = 0; scanf("%d",&T); 123 while (T--){ 124 scanf("%d%d",&n,&m); 125 for (int i = 0; i < n; i++) { 126 scanf("%s",s[i]); 127 } 128 init(); 129 printf("Case #%d: ",++cas); 130 solve(); 131 } 132 return 0; 133 }