题意:找一个出现了m次的最长子串,以及这时的最右的位置。
hash的话代码还是比较好写的,,但是时间比SA多很多。。
1 #include <stdio.h> 2 #include <algorithm> 3 #include <string.h> 4 using namespace std; 5 const int N = 40000 + 100; 6 typedef long long ll; 7 const int X = 257; 8 const int mod = (int)1e9 + 7; 9 10 char s[N]; 11 int m,len,pw[N]; 12 int H[N],pos; 13 14 struct node 15 { 16 int id,hash; 17 bool operator < (const node & temp) const 18 { 19 return hash == temp.hash ? id < temp.id : hash < temp.hash; 20 } 21 }p[N]; 22 23 bool solve(int L) 24 { 25 int cnt = 0; 26 pos = -1; 27 for(int i=1;i+L-1<=len;i++) 28 { 29 int id = i; 30 int hash = ((ll)H[i] - (ll)H[i+L]*pw[L]) % mod; 31 if(hash < 0) hash += mod; // 注意这里! 32 p[i] = (node){id, hash}; 33 } 34 sort(p+1, p+1+ len - L + 1); 35 36 for(int i=1;i<=len-L+1;i++) 37 { 38 if(i == 1 || p[i].hash != p[i-1].hash) cnt = 0; 39 if(++cnt >= m) pos = max(pos, p[i].id); 40 } 41 return pos != -1; 42 } 43 44 int main() 45 { 46 pw[0] = 1; 47 for(int i=1;i<N;i++) pw[i] = 1LL*pw[i-1] * X % mod; 48 while(scanf("%d",&m) == 1 && m) 49 { 50 scanf("%s",s+1); 51 len = strlen(s+1); 52 for(int i=len;i>=1;i--) H[i] = (1LL*H[i+1] * X + s[i] - 'a') % mod; 53 int l = 1, r = len, ans = -1; 54 while(l <= r) 55 { 56 int mid = l + r >> 1; 57 if(solve(mid)) ans = mid, l = mid + 1; 58 else r = mid - 1; 59 } 60 solve(ans); 61 if(ans != -1) printf("%d %d ",ans, pos-1); 62 else puts("none"); 63 } 64 return 0; 65 }
SA的话,写法需要细细体会一下了。。时间减少了很多。
1 #include <stdio.h> 2 #include <algorithm> 3 #include <string.h> 4 using namespace std; 5 const int N = 40000 + 100; 6 typedef long long ll; 7 8 /** 9 * sa[i]:表示排在第i位的后缀的起始下标 10 * rank[i]:表示后缀suffix(i)排在第几 11 * height[i]:sa[i-1] 与 sa[i]的LCP(最长公共前缀)值 12 * 13 * */ 14 /* 15 如果整数的话模板改成int. 16 加一个数a[n] = 0 。 这样他的排名是第一个。 17 construct(a,n+1); 18 19 字符串的话。 20 len = strlen(str); 21 construct(s,strlen(s)+1); 22 排名第0的是个空字符串。 23 24 height[i]:sa[i-1] 与 sa[i]的LCP(最长公共前缀)值 25 所以height[1] = 0; 26 rank[len] = 0; 27 sa[0] = len; 28 */ 29 int sa[N],rnk[N],height[N]; 30 void construct(const char *s,int n,int m = 256) { 31 static int t1[N],t2[N],c[N]; 32 int *x = t1,*y = t2; 33 int i,j,k,p,l; 34 for (i = 0; i < m; ++ i) c[i] = 0; 35 for (i = 0; i < n; ++ i) c[x[i] = s[i]] ++; 36 for (i = 1; i < m; ++ i) c[i] += c[i - 1]; 37 for (i = n - 1; i >= 0; -- i) sa[--c[x[i]]] = i; 38 for (k = 1; k <= n; k <<= 1) { 39 p = 0; 40 for (i = n - k; i < n; ++ i) y[p++] = i; 41 for (i = 0; i < n; ++ i) if (sa[i] >= k) y[p++] = sa[i] - k; 42 for (i = 0; i < m; ++ i) c[i] = 0; 43 for (i = 0; i < n; ++ i) c[x[y[i]]] ++; 44 for (i = 1; i < m; ++ i) c[i] += c[i - 1]; 45 for (i = n - 1; i >= 0; -- i) sa[--c[x[y[i]]]] = y[i]; 46 std::swap(x,y); 47 p = 1; x[sa[0]] = 0; 48 for (i = 1; i < n; ++ i) 49 x[sa[i]] = y[sa[i - 1]] == y[sa[i]] 50 && y[sa[i - 1] + k] == y[sa[i] + k] ? p - 1: p ++; 51 if (p >= n) break; 52 m = p; 53 } 54 for (i = 0; i < n; ++ i) rnk[sa[i]] = i; 55 for (i = 0,l = 0; i < n; ++ i) { 56 if (rnk[i]) { 57 j = sa[rnk[i] - 1]; 58 while (s[i + l] == s[j + l]) l++; 59 height[rnk[i]] = l; 60 if (l) l--; 61 } 62 } 63 } 64 65 int m,pos,len; 66 char s[N]; 67 bool solve(int L) 68 { 69 // 用后缀数组的话,如果重复长度就是最长的长度,需要特判 70 if(m == 1 && L == len) {pos = 0; return 1;} 71 pos = -1; 72 int cnt = 1; 73 int temp = -1; // 要用一个temp来记录当前cnt>=m的最大的位置 74 for(int i=1;i<=len;i++) 75 { 76 if(height[i] >= L) cnt++, temp = max(temp, sa[i]); 77 else cnt = 1, temp = sa[i]; 78 if(cnt >= m) pos = max(pos, temp); 79 } 80 return pos != -1; 81 } 82 83 int main() 84 { 85 while(scanf("%d",&m) == 1 && m) 86 { 87 scanf("%s",s); 88 len = strlen(s); 89 construct(s, len+1); 90 int l = 1, r = len; 91 int ans = -1; 92 while(l <= r) 93 { 94 int mid = l + r >> 1; 95 if(solve(mid)) ans = mid, l = mid + 1; 96 else r = mid - 1; 97 } 98 solve(ans); 99 if(ans != -1) printf("%d %d ",r,pos); 100 else puts("none"); 101 } 102 return 0; 103 }
顺便想说一下的是,这里不知为何下标从1开始无限WA,以后用SA还是下标从0开始好了。。毕竟不懂SA的具体原理。