后缀数组
具体请看论文....
不重叠的最长重复子串
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> using namespace std; #define N 20100 int p[N]; int sa[N],rank[N],height[N]; int wa[N],wb[N],c[N]; //sa[i] 表示排名为i的下标 //rank[i]表示i的排名 //height[i]表示sa[i]和sa[i-1]的最长前缀 void build_sa(int s[],int n,int m) { int i,j,p,*x = wa,*y = wb; //初始化基数排序 for(i = 0;i < m;i ++) c[i] = 0; for(i = 0;i < n;i ++) c[x[i] = s[i]] ++; for(i = 1;i < m;i ++) c[i] += c[i-1]; for(i = n-1;i >= 0;i --) sa[--c[x[i]]] = i; for(j = 1;j <= n;j <<= 1) { p = 0; //y[i] 表示第二关键字排名为i的下标 for(i = n-j;i < n;i ++) y[p++] = i; //如果后缀长度不到j的,第二关键字排名靠前 for(i = 0;i < n;i ++) if(sa[i] >= j) y[p++] = sa[i] - j; //sa[i] < j的时候不可能为第二关键字,位置sa[i]-j的第二关键字为sa[i], //他的排名为p //按第一关键字排序 for(i = 0;i < m;i ++) c[i] = 0; for(i = 0;i < n;i ++) c[x[y[i]]] ++; for(i = 1;i < m;i ++) c[i] += c[i-1]; //按y数组的存的下标顺序,基数排序 for(i = n-1;i >= 0;i --) sa[--c[x[y[i]]]] = y[i]; swap(x,y); //生成新的x数组,交换成y,是为了节省空间.... p = 1;x[sa[0]] = 0; //如果两个关键字都一样,下次的第一关键字一样。 for(i = 1;i < n;i ++) x[sa[i]] = y[sa[i-1]] == y[sa[i]]&&y[sa[i-1]+j] == y[sa[i]+j]?p-1:p++; if(p >= n) break; m = p; } } void get_height(int s[],int n) { int i,j,k = 0; for(i = 1;i <= n;i ++) rank[sa[i]] = i; //看论文,有证明 for(i = 0;i < n;i ++) { if(k) k--; j = sa[rank[i]-1]; while(s[i+k]==s[j+k]) k ++; height[rank[i]] = k; } } int judge(int mid,int n) { int minz,maxz,i; minz = maxz = sa[1]; for(i = 2;i <= n;i ++) { if(height[i] < mid) { minz = maxz = sa[i]; } else { maxz = max(maxz,sa[i]); minz = min(minz,sa[i]); if(maxz-minz > mid) return 1; } } return 0; } int main() { int n,i; while(scanf("%d",&n)!=EOF) { if(n == 0) break; for(i = 0;i < n;i ++) scanf("%d",&p[i]); for(i = 0;i < n-1;i ++) p[i] = p[i+1] - p[i] + 90; n --; p[n] = 0; build_sa(p,n+1,200); get_height(p,n); int str,mid,end; str = 1; end = n/2; while(str < end) { mid = (str+end + 1)/2; if(judge(mid,n)) str = mid; else end = mid-1; } if(end >= 4) printf("%d\n",end+1); else printf("0\n"); } return 0; }
数据非常水,本意是可重叠k次最长子串。
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> using namespace std; #define N 20100 int p[N]; int sa[N],rank[N],height[N]; int wa[N],wb[N],c[N]; void build_sa(int s[],int n,int m) { int i,j,p,*x = wa,*y = wb; for(i = 0;i < m;i ++) c[i] = 0; for(i = 0;i < n;i ++) c[x[i] = s[i]] ++; for(i = 1;i < m;i ++) c[i] += c[i-1]; for(i = n-1;i >= 0;i --) sa[--c[x[i]]] = i; for(j = 1;j <= n;j <<= 1) { p = 0; for(i = n-j;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 < m;i ++) c[i] = 0; for(i = 0;i < n;i ++) c[x[y[i]]] ++; for(i = 1;i < m;i ++) c[i] += c[i-1]; for(i = n-1;i >= 0;i --) sa[--c[x[y[i]]]] = y[i]; swap(x,y); p = 1;x[sa[0]] = 0; for(i = 1;i < n;i ++) x[sa[i]] = y[sa[i-1]] == y[sa[i]]&&y[sa[i-1]+j] == y[sa[i]+j]?p-1:p++; if(p >= n) break; m = p; } } void get_height(int s[],int n) { int i,j,k = 0; for(i = 1;i <= n;i ++) rank[sa[i]] = i; for(i = 0;i < n;i ++) { if(k) k--; j = sa[rank[i]-1]; while(s[i+k]==s[j+k]) k ++; height[rank[i]] = k; } } int judge(int mid,int n,int k) { int flag,i; flag = 1; for(i = 2;i <= n;i ++) { if(height[i] < mid) { if(flag >= k) return 1; flag = 1; } else { flag ++; } } if(flag >= k) return 1; else return 0; } int main() { int n,i,maxz,k; while(scanf("%d%d",&n,&k)!=EOF) { maxz = 0; for(i = 0;i < n;i ++) { scanf("%d",&p[i]); p[i] ++; maxz = max(maxz,p[i]); } p[n] = 0; build_sa(p,n+1,maxz+20); get_height(p,n); int str,mid,end; str = 1; end = n/2; while(str < end) { mid = (str+end + 1)/2; if(judge(mid,n,k)) str = mid; else end = mid-1; } printf("%d\n",end); } return 0; }
注意把C数组开到N....傻了...
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> using namespace std; #define N 200100 int sa[N],rank[N],height[N]; int c[N],wa[N],wb[N]; char s1[N],s2[N]; int p[N]; void build_sa(int s[],int n,int m) { int i,j,p,*x = wa,*y = wb; for(i = 0;i < m;i ++) c[i] = 0; for(i = 0;i < n;i ++) c[x[i] = s[i]] ++; for(i = 1;i < m;i ++) c[i] += c[i-1]; for(i = n-1;i >= 0;i --) sa[--c[x[i]]] = i; for(j = 1;j <= n;j <<= 1) { p = 0; for(i = n-j;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 < m;i ++) c[i] = 0; for(i = 0;i < n;i ++) c[x[y[i]]] ++; for(i = 1;i < m;i ++) c[i] += c[i-1]; for(i = n-1;i >= 0;i --) sa[--c[x[y[i]]]] = y[i]; swap(x,y); p = 1;x[sa[0]] = 0; for(i = 1;i < n;i ++) x[sa[i]] = y[sa[i-1]] == y[sa[i]]&&y[sa[i-1]+j] == y[sa[i]+j]?p-1:p++; if(p >= n) break; m = p; } } void get_height(int s[],int n) { int i,j,k = 0; for(i = 1;i <= n;i ++) rank[sa[i]] = i; for(i = 0;i < n;i ++) { if(k) k --; j = sa[rank[i]-1]; while(s[i+k] == s[j+k]) k ++; height[rank[i]] = k; } } int main() { int i,len1,len2,j,n; scanf("%s%s",s1,s2); len1 = strlen(s1); j = 0; for(i = 0;i < len1;i ++) p[j++] = s1[i]-'a'+1; p[j++] = 28; len2 = strlen(s2); for(i = 0;i < len2;i ++) p[j++] = s2[i]-'a'+1; p[j] = 0; n = j; build_sa(p,n+1,30); get_height(p,n); int ans = 0; for(i = 1;i <= n;i ++) { if(sa[i] < len1&&sa[i-1] > len1) ans = max(ans,height[i]); if(sa[i] > len1&&sa[i-1] < len1) ans = max(ans,height[i]); } printf("%d\n",ans); return 0; }
感觉复杂度挺高的,n=1的时候需要特判。写的代码有些乱....
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> using namespace std; #define N 200100 int sa[N],height[N],rank[N]; int c[N],wa[N],wb[N]; int p[N]; char str[101][1101]; int flag[N]; int o[101]; int t; void build_sa(int s[],int n,int m) { int i,j,p,*x = wa,*y = wb; for(i = 0; i < m; i ++) c[i] = 0; for(i = 0; i < n; i ++) c[x[i] = s[i]] ++; for(i = 1; i < m; i ++) c[i] += c[i-1]; for(i = n-1; i >= 0; i --) sa[--c[x[i]]] = i; for(j = 1; j <= n; j <<= 1) { p = 0; for(i = n-j; 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 < m; i ++) c[i] = 0; for(i = 0; i < n; i ++) c[x[y[i]]] ++; for(i = 1; i < m; i ++) c[i] += c[i-1]; for(i = n-1; i >= 0; i --) sa[--c[x[y[i]]]] = y[i]; swap(x,y); p = 1; x[sa[0]] = 0; for(i = 1; i < n; i ++) x[sa[i]] = y[sa[i-1]] == y[sa[i]]&&y[sa[i-1]+j] == y[sa[i]+j]?p-1:p++; if(p >= n) break; m = p; } } void get_height(int s[],int n) { int i,j,k = 0; for(i = 1; i <= n; i ++) rank[sa[i]] = i; for(i = 0; i < n; i ++) { if(k) k --; j = sa[rank[i]-1]; while(s[i+k] == s[j+k]) k ++; height[rank[i]] = k; } } int judge(int mid,int n) { int i,j; memset(o,0,sizeof(o)); o[flag[sa[1]]] = 1; for(i = 2; i <= n; i ++) { if(height[i] < mid) { int temp = 0; for(j = 1; j <= t; j ++) { if(o[j]) temp ++; o[j] = 0; } if(temp > t/2) return 1; o[flag[sa[i]]] = 1; } else { o[flag[sa[i]]] = 1; } } int temp = 0; for(j = 1; j <= t; j ++) { if(o[j]) temp ++; o[j] = 0; } if(temp > t/2) return 1; return 0; } void fun(int key,int end) { int temp = 0,i; for(i = 1; i <= t; i ++) { if(o[i]) temp ++; o[i] = 0; } if(temp > t/2) { for(i = 0; i < end; i ++) { printf("%c",p[sa[key]+i]-1+'a'); } printf("\n"); } } int main() { int n,i,j,len,num,temp,f; f = 0; while(scanf("%d",&n)!=EOF) { t = n; if(f)printf("\n"); f = 1; if(n == 0) break; num = 0; temp = 30; for(i = 0; i < n; i ++) { scanf("%s",str[i]); len = strlen(str[i]); for(j = 0; j < len; j ++) { flag[num] = i+1; p[num++] = str[i][j]-'a'+1; } flag[num] = 0; if(i != n-1) p[num++] = temp++; else p[num] = 0; } if(n == 1) { printf("%s\n",str[0]); continue; } build_sa(p,num+1,temp+10); get_height(p,num); int str,end,mid; str = 0; end = 5000; //for(i = 1;i <= num;i ++) //printf("%d ",height[i]); while(str < end) { mid = (str + end + 1)/2; if(judge(mid,num)) str = mid; else end = mid - 1; } if(end == 0) { printf("?\n"); continue; } int sz[101]; memset(sz,0,sizeof(sz)); memset(o,0,sizeof(o)); o[flag[sa[1]]] = 1; for(i = 2; i <= num; i ++) { if(height[i] < end) { fun(i-1,end); o[flag[sa[i]]] = 1; } else { o[flag[sa[i]]] = 1; } } fun(num,end); } return 0; }
最长回文串,跑的有些慢...
这份代码 是水过的,我找的是相邻的最大值并且两个后缀必须在前面或后面,但是abcdba会把abba当成回闻....这题如果改成rmq的话,目测会MLE+TLE,放弃治疗了。
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <cmath> using namespace std; #define N 2010000 char str[1000001]; int sa[N],height[N],rank[N]; int p[N],c[N],wa[N],wb[N]; void build_sa(int s[],int n,int m) { int i,j,p,*x = wa,*y = wb; for(i = 0;i < m;i ++) c[i] = 0; for(i = 0;i < n;i ++) c[x[i] = s[i]] ++; for(i = 1;i < m;i ++) c[i] += c[i-1]; for(i = n-1;i >= 0;i --) sa[--c[x[i]]] = i; for(j = 1;j <= n;j <<= 1) { p = 0; for(i = n-j;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 < m;i ++) c[i] = 0; for(i = 0;i < n;i ++) c[x[y[i]]] ++; for(i = 1;i < m;i ++) c[i] += c[i-1]; for(i = n-1;i >= 0;i --) sa[--c[x[y[i]]]] = y[i]; swap(x,y); p = 1; x[sa[0]] = 0; for(i = 1;i < n;i ++) x[sa[i]] = y[sa[i-1]] == y[sa[i]]&&y[sa[i-1]+j] == y[sa[i]+j]?p-1:p++; if(p >= n) break; m = p; } } void get_height(int s[],int n) { int i,j,k = 0; for(i = 1;i <= n;i ++) rank[sa[i]] = i; for(i = 0;i < n;i ++) { j = sa[rank[i]-1]; if(k) k--; while(s[i+k] == s[j+k]) k ++; height[rank[i]] = k; } } int main() { int i,len,n,cas = 1; while(scanf("%s",str)!=EOF) { if(strcmp(str,"END") == 0) break; n = 0; len = strlen(str); for(i = 0;i < len;i ++) p[n++] = str[i]-'a'+1; p[n++] = 28; for(i = 0;i < len;i ++) p[n++] = str[len-i-1]-'a'+1; p[n] = 0; build_sa(p,n+1,30); get_height(p,n); int ans = 0; for(i = 1;i <= n;i ++) { if(sa[i-1] < len||sa[i] > len) ans = max(ans,height[i]); if(sa[i-1] > len||sa[i] < len) ans = max(ans,height[i]); } printf("Case %d: %d\n",cas++,ans); } return 0; }
最长回文串,这是正解。rmq+后缀数组,调试了好一会,费劲啊!!
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <cmath> using namespace std; #define N 2010000 int sa[N],height[N],Rank[N]; int c[N],p[N],wa[N],wb[N]; int bin[31]; int minz[22][N]; char str[N]; void build_sa(int s[],int n,int m) { int i,j,p,*x = wa,*y = wb; for(i = 0; i < m; i ++) c[i] = 0; for(i = 0; i < n; i ++) c[x[i] = s[i]] ++; for(i = 1; i < m; i ++) c[i] += c[i-1]; for(i = n-1; i >= 0; i --) sa[--c[x[i]]] = i; for(j = 1; j <= n; j <<= 1) { p = 0; for(i = n-j; 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 < m; i ++) c[i] = 0; for(i = 0; i < n; i ++) c[x[y[i]]] ++; for(i = 1; i < m; i ++) c[i] += c[i-1]; for(i = n-1; i >= 0; i --) sa[--c[x[y[i]]]] = y[i]; swap(x,y); p = 1; x[sa[0]] = 0; for(i = 1; i < n; i ++) x[sa[i]] = y[sa[i-1]] == y[sa[i]]&&y[sa[i-1]+j] == y[sa[i]+j]?p-1:p++; if(p >= n) break; m = p; } } void get_height(int s[],int n) { int i,j,k = 0; for(i = 1; i <= n; i ++) Rank[sa[i]] = i; for(i = 0; i < n; i ++) { if(k) k --; j = sa[Rank[i]-1]; while(s[i+k] == s[j+k]) k ++; height[Rank[i]] = k; } } void init(int n) { int i,j; for(i = 1; i <= n; i ++) minz[0][i] = height[i]; for(i = 1; bin[i] <= n; i ++) { for(j = 1; j + bin[i-1] <= n; j ++) { minz[i][j] = min(minz[i-1][j],minz[i-1][j+bin[i-1]]); } } } int rmq(int s,int t) { int k = log((t-s+1)*1.0)/log(2.0); return min(minz[k][s],minz[k][t-bin[k]+1]); } int main() { int i,len,n; scanf("%s",str); n = 0; len = strlen(str); for(i = 0; i < len; i ++) p[n++] = str[i]+1; p[n++] = 299; for(i = 0; i < len; i ++) p[n++] = str[len-i-1]+1; p[n] = 0; build_sa(p,n+1,300); get_height(p,n); for(i = 0; i < 21; i ++) bin[i] = 1<<i; init(n); int s,t,temp,pos,ans = 0; for(i = 0; i < len; i ++) { s = Rank[i+1]; t = Rank[2*len-i]; if(s > t) swap(s,t); s ++; temp = rmq(s,t)*2; if(ans < temp) { ans = temp; pos = i - temp/2 + 1; } s = Rank[i]; t = Rank[2*len-i]; if(s > t) swap(s,t); s ++; temp = rmq(s,t)*2 - 1; if(ans < temp) { ans = temp; pos = i - temp/2; } } for(i = 0;i < ans;i ++) printf("%c",str[pos+i]); return 0; }
POJ 3693 Maximum repetition substring
可能是水过,看论文上的讲解,完全还是不会啊....
枚举那里看懂了,寻找L*i和L*(i+1)往前最长和往后最长,不知道怎么操作的。不会是把字符串倒过来然后再处理一遍吧。。。往后的很简单,就是height最小值,往前就有点扯了,知道后面的长度x,如果前面的长度大于L- x%L那么就会总长度会+1,总长度+2那些个情况应该是不用考虑的,枚举前面的时候已经算了。还有字典序,完全是乱搞,看了一个大神写的是吧L保存下来,然后枚举sa,判断是否可行.....做个题真难啊....
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <cmath> using namespace std; #define N 100100 int bin[30]; int sa[N],height[N],rank[N]; int c[N],p[N],wa[N],wb[N]; char str[N]; int minz[22][N]; int que[10001]; void build_sa(int s[],int n,int m) { int i,j,p,*x = wa,*y = wb; for(i = 0;i < m;i ++) c[i] = 0; for(i = 0;i < n;i ++) c[x[i] = s[i]] ++; for(i = 1;i < m;i ++) c[i] += c[i-1]; for(i = n-1;i >= 0;i --) sa[--c[x[i]]] = i; for(j = 1;j <= n;j <<= 1) { p = 0; for(i = n-j;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 < m;i ++) c[i] = 0; for(i = 0;i < n;i ++) c[x[y[i]]] ++; for(i = 1;i < m;i ++) c[i] += c[i-1]; for(i = n-1;i >= 0;i --) sa[--c[x[y[i]]]] = y[i]; swap(x,y); p = 1; x[sa[0]] = 0; for(i = 1;i < n;i ++) x[sa[i]] = y[sa[i-1]] == y[sa[i]]&&y[sa[i-1]+j] == y[sa[i]+j]?p-1:p++; if(p >= n) break; m = p; } } void get_height(int s[],int n) { int i,j,k = 0; for(i = 1;i <= n;i ++) rank[sa[i]] = i; for(i = 0;i < n;i ++) { if(k) k --; j = sa[rank[i]-1]; while(s[i+k] == s[j+k]) k ++; height[rank[i]] = k; } } void init(int n) { int i,j; for(i = 1;i <= n;i ++) minz[0][i] = height[i]; for(i = 1;bin[i] <= n;i ++) { for(j = 1;j +bin[i-1] <= n;j ++) { minz[i][j] = min(minz[i-1][j],minz[i-1][j+bin[i-1]]); } } } int rmq(int s,int t) { s = rank[s]; t = rank[t]; if(s > t) swap(s,t); s ++; int k = log((t-s+1)*1.0)/log(2.0); return min(minz[k][s],minz[k][t-bin[k]+1]); } int main() { int cas = 1,len,i,j; for(i = 0;i <= 20;i ++) bin[i] = 1<<i; while(scanf("%s",str)!=EOF) { if(str[0] == '#') break; len = strlen(str); for(i = 0;i < len;i ++) p[i] = str[i] - 'a'+1; p[len] = 0; build_sa(p,len+1,30); get_height(p,len); init(len); int ans = 0,num = 0; for(i = 1;i <= len;i ++) { for(j = 0;j < len;j += i) { if(j+i >= len) continue; int temp,lf,res; res = 0; temp = rmq(j,j+i); res = max(res,temp/i+1); lf = i - temp%i; if(j-lf >= 0) { temp = rmq(j-lf,j-lf+i); if(temp >= lf) res = max(res,temp/i+1); } if(ans < res) { ans = res; num = 0; que[num++] = i; } else if(ans == res) { if(que[num-1] == i) continue; que[num++] = i; } } } int pos,lt; for(i = 1;i <= len;i ++) { for(j = 0;j < num;j ++) { int temp = que[j]; if(rmq(sa[i],sa[i]+temp) >= (ans-1)*temp) { pos = sa[i]; lt = temp*ans; break; } } if(j != num) break; } printf("Case %d: ",cas++); for(i = 0;i < lt;i ++) printf("%c",str[pos+i]); printf("\n"); } return 0; }
很棒的一个题,把一个数字串分为三部分,翻转。特别注意第一个数字最大,倒序之后,最小的后缀,一定是最小的数字开头的,因为最后一个数字为最大,不存在下面这种情况。
后面就是分成两段的情况了,选最小并不一定对。
如 5 0 5 0 2 3 倒过来之后 3 2 0 5 0 5
0 5
0 5 0 5
...
这样选最小的并不一定最好, 0 5 和0 5 0 5前面是一样的。0 5后面其实要比较后一部分的字典序。
做法看这个博客:http://www.cnblogs.com/zcwwzdjn/archive/2012/03/10/2389413.html
就是再复制一遍,注意保证分成两部分sa[i]>=1,这题不用中间隔开字符,要理解思想啊....
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <cmath> using namespace std; #define N 201000 int sa[N],height[N],rank[N]; int c[N],wa[N],wb[N]; int p[N],que[N]; int ans[4]; struct node { int x,id; } num[N]; bool cmp(node a,node b) { return a.x < b.x; } void build_sa(int s[],int n,int m) { int i,j,p,*x = wa,*y = wb; for(i = 0; i < m; i ++) c[i] = 0; for(i = 0; i < n; i ++) c[x[i] = s[i]] ++; for(i = 1; i < m; i ++) c[i] += c[i-1]; for(i = n-1; i >= 0; i --) sa[--c[x[i]]] = i; for(j = 1; j <= n; j <<= 1) { p = 0; for(i = n-j; 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 < m; i ++) c[i] = 0; for(i = 0; i < n; i ++) c[x[y[i]]] = 0; for(i = 1; i < m; i ++) c[i] += c[i-1]; for(i = n-1; i >= 0; i --) sa[--c[x[y[i]]]] = y[i]; swap(x,y); p = 1; x[sa[0]] = 0; for(i = 1; i < n; i ++) x[sa[i]] = y[sa[i-1]] == y[sa[i]]&&y[sa[i-1]+j] == y[sa[i]+j]?p-1:p++; if(p >= n) break; m = p; } } int main() { int n,i,temp,tn; scanf("%d",&n); tn = n; for(i = 0; i < n; i ++) { scanf("%d",&num[i].x); que[n-i-1] = num[i].x; num[i].id = n-i-1; } sort(num,num+n,cmp); temp = 2; p[num[0].id] = 1; for(i = 1; i < n; i ++) { p[num[i].id] = num[i-1].x == num[i].x?temp-1:temp++; } p[n] = 0; build_sa(p,n+1,temp+10); ans[0] = 1000000; for(i = 1; i <= n; i ++) { if(sa[i] >= 2) { ans[1] = sa[i]; break; } } n = ans[1]; for(i = 0; i < ans[1]; i ++) p[n++] = p[i]; p[n] = 0; build_sa(p,n+1,temp+10); for(i = 1; i <= n; i ++) { if(sa[i] < ans[1]&&sa[i] >= 1) { ans[2] = sa[i]; break; } } for(i = ans[1]; i < tn; i ++) printf("%d\n",que[i]); for(i = ans[2]; i < ans[1]; i ++) printf("%d\n",que[i]); for(i = 0; i < ans[2]; i ++) printf("%d\n",que[i]); return 0; }
HDU 4416 Good Article Good sentence
RE了很多次....注意n = 0,数组开小,最后结果LL。
A出现过,B[]没出现的字串。
论文里有求子串的个数,这题麻烦一点,后缀排序后对于sa[i] 要减去前面出现过的和后面出现过的。前面出现过的值肯定是height[i],无论sa[i-1]是A里面的,还是B[]里面的,height[i]是最大值,保证A#B...保证这个#是字典序是次小的,保证了A的相对顺序不变, 再从j+1寻找第一个B[]的后缀,那么rmq(i+1,pos)就是sa[i]后面出现过的,取 max就好,那个rmq从后往前倒着标记一遍就可以求出来。
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <cmath> using namespace std; #define N 501000 #define LL __int64 int sa[N],height[N],rank[N]; int c[N],wa[N],wb[N]; int p[N]; int res[N]; char str[N]; void build_sa(int s[],int n,int m) { int i,j,p,*x = wa,*y = wb; for(i = 0;i < m;i ++) c[i] = 0; for(i = 0;i < n;i ++) c[x[i] = s[i]] ++; for(i = 1;i < m;i ++) c[i] += c[i-1]; for(i = n-1;i >= 0;i --) sa[--c[x[i]]] = i; for(j = 1;j <= n;j <<= 1) { p = 0; for(i = n-j;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 < m;i ++) c[i] = 0; for(i = 0;i < n;i ++) c[x[y[i]]] ++; for(i = 1;i < m;i ++) c[i] += c[i-1]; for(i = n-1;i >= 0;i --) sa[--c[x[y[i]]]] = y[i]; swap(x,y); p = 1;x[sa[0]] = 0; for(i = 1;i < n;i ++) x[sa[i]] = y[sa[i-1]] == y[sa[i]]&&y[sa[i-1]+j] == y[sa[i]+j]?p-1:p++; if(p >= n) break; m = p; } } void get_height(int s[],int n) { int i,j,k = 0; for(i = 1;i <= n;i ++) rank[sa[i]] = i; for(i = 0;i < n;i ++) { if(k) k--; j = sa[rank[i]-1]; while(s[i+k]==s[j+k]) k ++; height[rank[i]] = k; } } int main() { int t,cas = 1,i,temp,len,tlen,num; int n,j; scanf("%d",&t); while(t--) { scanf("%d",&n); num = 0; for(i = 0;i <= n;i ++) { scanf("%s",str); if(i == 0) len = strlen(str); tlen = strlen(str); for(j = 0;j < tlen;j ++) { p[num++] = str[j]-'a'+2+n; } if(i == n) p[num] = 0; else p[num++] = i+1; } build_sa(p,num+1,n+30); get_height(p,num); LL ans = 0; temp = 0; for(i = num;i >= 1;i --) { if(sa[i] < len) { res[i] = temp; temp = min(height[i],res[i]); } else temp = height[i]; } for(i = 1;i <= num;i ++) { if(sa[i] < len) { ans += len - sa[i] - max(height[i],res[i]); } } printf("Case %d: %I64d\n",cas++,ans); } return 0; }
水题....
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <cmath> using namespace std; #define N 1100 #define LL __int64 int sa[N],height[N],rank[N]; int c[N],wa[N],wb[N]; int p[N]; char str[N]; void build_sa(int s[],int n,int m) { int i,j,p,*x = wa,*y = wb; for(i = 0;i < m;i ++) c[i] = 0; for(i = 0;i < n;i ++) c[x[i] = s[i]] ++; for(i = 1;i < m;i ++) c[i] += c[i-1]; for(i = n-1;i >= 0;i --) sa[--c[x[i]]] = i; for(j = 1;j <= n;j <<= 1) { p = 0; for(i = n-j;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 < m;i ++) c[i] = 0; for(i = 0;i < n;i ++) c[x[y[i]]] ++; for(i = 1;i < m;i ++) c[i] += c[i-1]; for(i = n-1;i >= 0;i --) sa[--c[x[y[i]]]] = y[i]; swap(x,y); p = 1; x[sa[0]] = 0; for(i = 1;i < n;i ++) x[sa[i]] = y[sa[i-1]] == y[sa[i]]&&y[sa[i-1]+j] == y[sa[i]+j]?p-1:p++; if(p >= n) break; m = p; } } void get_height(int s[],int n) { int i,j,k = 0; for(i = 1;i <= n;i ++) rank[sa[i]] = i; for(i = 0;i < n;i ++) { if(k) k --; j = sa[rank[i]-1]; while(s[i+k] == s[j+k]) k ++; height[rank[i]] = k; } } int main() { int i,j,len,maxz,minz; while(scanf("%s",str)!=EOF) { if(str[0] == '#') break; len = strlen(str); for(i = 0;i < len;i ++) p[i] = str[i] - 'a' + 1; p[len] = 0; build_sa(p,len+1,30); get_height(p,len); int ans = 0; for(i = 1;i <= len;i ++) { maxz = sa[1]; minz = sa[1]; for(j = 2;j <= len;j ++) { if(height[j] < i) { if(minz+i <= maxz) ans ++; maxz = sa[j]; minz = sa[j]; } else { maxz = max(maxz,sa[j]); minz = min(minz,sa[j]); } } if(minz+i <= maxz) ans ++; } printf("%d\n",ans); } return 0; }
注意n = 1的特殊情况。
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <cmath> using namespace std; #define N 100000 int sa[N],height[N],rank[N]; int c[N],wa[N],wb[N]; int p[N]; char str[N]; int m; void build_sa(int s[],int n,int m) { int i,j,p,*x = wa,*y = wb; for(i = 0; i < m; i ++) c[i] = 0; for(i = 0; i < n; i ++) c[x[i] = s[i]] ++; for(i = 1; i < m; i ++) c[i] += c[i-1]; for(i = n-1; i >= 0; i --) sa[--c[x[i]]] = i; for(j = 1; j <= n; j <<= 1) { p = 0; for(i = n-j; 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 < m; i ++) c[i] = 0; for(i = 0; i < n; i ++) c[x[y[i]]] ++; for(i = 1; i < m; i ++) c[i] += c[i-1]; for(i = n-1; i >= 0; i --) sa[--c[x[y[i]]]] = y[i]; swap(x,y); p = 1; x[sa[0]] = 0; for(i = 1; i < n; i ++) x[sa[i]] = y[sa[i-1]] == y[sa[i]]&&y[sa[i-1]+j] == y[sa[i]+j]?p-1:p++; if(p >= n) break; m = p; } } void get_height(int s[],int n) { int i,j,k = 0; for(i = 1; i <= n; i ++) rank[sa[i]] = i; for(i = 0; i < n; i ++) { if(k) k --; j = sa[rank[i]-1]; while(s[i+k] == s[j+k]) k ++; height[rank[i]] = k; } } int judge(int mid,int n) { int i,flag = 1; for(i = 1; i <= n; i ++) { if(height[i] < mid) { if(flag >= m) return 1; flag = 1; } else flag ++; } if(flag >= m) return 1; else return 0; } int main() { int i,len; while(scanf("%d",&m)!=EOF) { if(m == 0) break; scanf("%s",str); len = strlen(str); if(m == 1) { printf("%d %d\n",len,0);//特判 continue; } for(i = 0; i < len; i ++) { p[i] = str[i] - 'a' + 1; } p[len] = 0; build_sa(p,len+1,30); get_height(p,len); int str,end,mid; str = 0; end = len; while(str < end) { mid = (str + end + 1)/2; if(judge(mid,len)) str = mid; else end = mid - 1; } if(end == 0) { printf("none\n"); continue; } printf("%d ",end); int flag = 1,temp = sa[1],ans; ans = 0; for(i = 1; i <= len; i ++) { if(height[i] < end) { if(flag >= m) ans = max(ans,temp); temp = sa[i]; flag = 1; } else { temp = max(temp,sa[i]); flag ++; } } if(flag >= m) ans = max(ans,temp); printf("%d\n",ans); } return 0; }
想了好一会,发现 不会啊。。。。原来看错题了。。。这个比较只是相邻的....挺水的....
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <cmath> using namespace std; #define N 300000 #define LL __int64 int sa[N],height[N],rank[N]; int c[N],wa[N],wb[N]; int p[N],sum[N]; int minz[22][N]; int bin[22]; char str[N]; void build_sa(int s[],int n,int m) { int i,j,p,*x = wa,*y = wb; for(i = 0;i < m;i ++) c[i] = 0; for(i = 0;i < n;i ++) c[x[i] = s[i]] ++; for(i = 1;i < m;i ++) c[i] += c[i-1]; for(i = n-1;i >= 0;i --) sa[--c[x[i]]] = i; for(j = 1;j <= n;j <<= 1) { p = 0; for(i = n-j;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 < m;i ++) c[i] = 0; for(i = 0;i < n;i ++) c[x[y[i]]] ++; for(i = 1;i < m;i ++) c[i] += c[i-1]; for(i = n-1;i >= 0;i --) sa[--c[x[y[i]]]] = y[i]; swap(x,y); p = 1; x[sa[0]] = 0; for(i = 1;i < n;i ++) x[sa[i]] = y[sa[i-1]] == y[sa[i]]&&y[sa[i-1]+j] == y[sa[i]+j]?p-1:p++; if(p >= n) break; m = p; } } void get_height(int s[],int n) { int i,j,k = 0; for(i = 1;i <= n;i ++) rank[sa[i]] = i; for(i = 0;i < n;i ++) { if(k) k --; j = sa[rank[i]-1]; while(s[i+k] == s[j+k]) k ++; height[rank[i]] = k; } } void init(int n) { int i,j; for(i = 1;i <= n;i ++) minz[0][i] = height[i]; for(i = 1;i <= bin[i];i ++) { for(j = 1;j + bin[i-1] <= n;j ++) { minz[i][j] = min(minz[i-1][j],minz[i-1][j+bin[i-1]]); } } } int rmq(int s,int t) { s = rank[s];t = rank[t]; if(s > t) swap(s,t); s ++; int k = log((t-s+1)*1.0)/log(2.0); return min(minz[k][s],minz[k][t-bin[k]+1]); } int fun(int num) { int ans = 0; if(num == 0) return 1; while(num) { num /= 10; ans ++; } return ans; } int main() { int i,len,n,l,r,pl,pr; for(i = 0;i <= 20;i ++) bin[i] = 1<<i; while(scanf("%s",str)!=EOF) { len = strlen(str); for(i = 0;i < len;i ++) { p[i] = str[i] - 'a' + 1; } p[len] = 0; build_sa(p,len+1,30); get_height(p,len); init(len); scanf("%d",&n); LL ans1,ans2; int num,temp; ans1 = ans2 = 0; pl = pr = 0; for(i = 0;i < n;i ++) { scanf("%d%d",&l,&r); ans1 += r - l + 1; if(pl == l) temp = len - l; else temp = rmq(pl,l); num = min(pr-pl,min(temp,r-l)); ans2 += fun(num) + 2 + r - l - num; pl = l; pr = r; } printf("%I64d %I64d\n",ans1,ans2); } return 0; }