3043: 取个标题好难
Time Limit(Common/Java):6000MS/18000MS Memory Limit:65536KByte
Total Submit: 17 Accepted:4
Total Submit: 17 Accepted:4
Description
你是否经常在写完文章之后为文章取一个合适的标题而苦恼?这里提供一个很有趣的方法。
首先,标题应该概括文章的内容。为了简单起见,如果一个短语在文章中不重叠地出现了至少k次,那么这个短语就可以作为标题。例如,”dadadad”中,短语”dad” 不重叠地出现了两次,而不是三次。
其次,标题越长越好。长标题才能吸引眼球。
最后,这件事应该让计算机来做比较省事。
所以,请编写一个程序,根据给定的k和一段文章,给这篇文章取个标题。
Input
输入包括多组数据。
每组数据第一行为整数k,第二行为一个字符串表示文章内容,为了简化问题,字符串仅含小写字母。字符串长度不超过10000。
输入数据以k=0结束。
Output
对每组数据输出最长的标题长度,如果找不到符合要求的标题,则输出0。
Sample Input
3
abababa
1
apple
4
abababa
5
abababa
0
Sample Output
2
5
1
0
最长的出现次数>=k的不重复子串长度,这个题目明显可以hash,然后在二分长度,排序的时候将位置也标记下,最后判断下就可以了
#include<bits/stdc++.h> using namespace std; typedef unsigned long long ulint; #define maxn 23333 const ulint x=233; int M; char in[maxn]; ulint has[maxn]; int pos[maxn]; int id[maxn]; ulint xp[maxn]; ulint H[maxn]; bool cmp(int a,int b) { return has[a]<has[b]||has[a]==has[b]&&a<b; } bool check(int mid,int n) { pos[mid]=0; id[0]=0; for(int i=0; i+mid-1<n; i++)//就算每一段的hash值 id[i]=i,has[i]=H[i]-H[i+mid]*xp[mid]; sort(id,id+n-mid+1,cmp);//按hash值排序 int cnt=1; bool ret=0; int tmp=id[0]; for(int i=1; i<=n-mid; i++) if(has[id[i]]==has[id[i-1]]) { if(id[i]-tmp>=mid) cnt++,tmp=id[i]; if(cnt>=M) pos[mid]=max(pos[mid],tmp),ret=1; } else cnt=1,tmp=id[i]; return ret; } int main() { xp[0]=1; for(int i=1; i<maxn; i++)//预处理x次幂项 xp[i]=xp[i-1]*x; while(scanf("%d",&M),M) { scanf("%s",in); int n=strlen(in); if(M==1) { printf("%d ",n); continue; } H[n]=0; for(int i=n-1; i >= 0; i--)//预处理H[i]=s[i]+s[i+1]*x+...+s[n]*x^(n-i) H[i]=H[i+1]*x+(in[i]-'a'); int L=1,R=n,mid,len=0; while(L<=R) { mid=(L+R)>>1; if(check(mid,n)) { len=mid; L=mid+1; } else R=mid-1; } if(len==0) printf("0 "); else printf("%d ",len); } return 0; }
然后也可以后缀数组,我们之前那个题允许重复,直接判断height即可,这个题目要保存sa[i],然后排序判断是否重复
#include <stdio.h> #include <iostream> #include <algorithm> #include <string.h> #include <set> #define dbg(x) std::cout<<#x<<" = "<< (x)<< " " #define maxn 20005 int wa[maxn],wb[maxn],wv[maxn],ws[maxn],m; int cmp(int *r,int a,int b,int l) { return r[a]==r[b]&&r[a+l]==r[b+l]; } void getsa(int *r,int *sa,int n,int m) { int i,j,p,*x=wa,*y=wb,*t; for(i=0; i<m; i++) ws[i]=0; for(i=0; i<n; i++) ws[x[i]=r[i]]++; for(i=1; i<m; i++) ws[i]+=ws[i-1]; for(i=n-1; i>=0; i--) sa[--ws[x[i]]]=i; for(j=1,p=1; p<n; j*=2,m=p) { for(p=0,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<n; i++) wv[i]=x[y[i]]; for(i=0; i<m; i++) ws[i]=0; for(i=0; i<n; i++) ws[wv[i]]++; for(i=1; i<m; i++) ws[i]+=ws[i-1]; for(i=n-1; i>=0; i--) sa[--ws[wv[i]]]=y[i]; for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1; i<n; i++) x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++; } } int rank[maxn],height[maxn]; void calheight(int *r,int *sa,int n) { int i,j,k=0; for(i=1; i<=n; i++) rank[sa[i]]=i; for(i=0; i<n; height[rank[i++]]=k) for(k?k--:0,j=sa[rank[i]-1]; r[i+k]==r[j+k]; k++); } int a[maxn]; int check(int *sa,int n,int k) { int i,cnt=1; a[0]=sa[1]; for(i=2; i<=n; i++) { if(height[i]>=k) { a[cnt++]=sa[i]; } else { if(cnt>=m) { std::sort(a,a+cnt); int x=a[0],tot=cnt; for(int i=1; i<tot; i++) { if(a[i]-x<k)cnt--; else x=a[i]; } if(cnt>=m)return 1; } cnt=1,a[0]=sa[i]; } } std::sort(a,a+cnt); int x=a[0],tot=cnt; for(int i=1; i<tot; i++) { if(a[i]-x<k)cnt--; else x=a[i]; } if(cnt>=m)return 1; return 0; } int r[maxn],sa[maxn]; char str[maxn]; int main() { //freopen("test.in","r",stdin); while(scanf("%d",&m),m) { scanf("%s",str); int n=strlen(str); if(m==1) { printf("%d ",n); continue; } for(int i=0; i<n; i++)r[i]=str[i]; r[n]=0; getsa(r,sa,n+1,300); calheight(r,sa,n); int L=1,R=n,mi,len=0; while(L<=R) { mi=(L+R)>>1; if(check(sa,n,mi))L=mi+1,len=mi; else R=mi-1; } printf("%d ",len); } return 0; }