CCF 201509-5 最佳文章
问题描述
小明最近在研究一门新的语言,叫做Q语言。Q语言单词和文章都可以用且仅用只含有小写英文字母的字符串表示,任何由这些字母组成的字符串也都是一篇合法的Q语言文章。
在Q语言的所有单词中,小明选出了他认为最重要的n个。使用这些单词,小明可以评价一篇Q语言文章的“重要度”。
文章“重要度”的定义为:在该文章中,所有重要的Q语言单词出现次数的总和。其中多次出现的单词,不论是否发生包含、重叠等情况,每次出现均计算在内。
例如,假设n = 2,小明选出的单词是gvagv和agva。在文章gvagvagvagv中,gvagv出现了3次,agva出现了2次,因此这篇文章的重要度为3+2=5。
现在,小明想知道,一篇由m个字母组成的Q语言文章,重要度最高能达到多少。
输入格式
输入的第一行包含两个整数n, m,表示小明选出的单词个数和最终文章包含的字母个数。
接下来n行,每行包含一个仅由英文小写字母构成的字符串,表示小明选出的这n个单词。
输出格式
输出一行一个整数,表示由m个字母组成的Q语言文章中,重要度最高的文章的重要度。
样例输入
3 15
agva
agvagva
gvagva
样例输出
11
样例说明
15个字母组成的重要度最高的文章为gvagvagvagvagva。
在这篇文章中,agva出现4次,agvagva出现3次,gvagva出现4次,共计4+3+4=11次。
评测用例规模与约定
在评测时将使用10个评测用例对你的程序进行评测。
设s为构成n个重要单词字母的总个数,例如在样例中,s=4+7+6=17;a为构成n个重要单词字母的种类数,例如在样例中,共有3中字母’a’,’g’,’v’,因此a=3。
评测用例1和2满足2 ≤ n ≤ 3,1500 ≤ m ≤ 2000,s = 40;
评测用例3和4满足m = 20,2 ≤ a ≤ 3;
评测用例5、6和7满足2000 ≤ m ≤ 100000;
评测用例8满足n = 2;
所有的评测用例满足1 ≤ s ≤ 100,1 ≤ m ≤ 1015,每个单词至少包含1个字母,保证单词中仅出现英文小写字母,输入中不含多余字符,不会出现重复的单词。
#include<queue> #include<stdio.h> //#include<iostream> //#define debug(x) std::cerr<<#x<<" "<<x<<'\n'; const int N=105; const int M=1e5+5; const int sz=26; int n,m,ans,cnt=1,now,p,fail[N],tag[N],tr[N][sz];char s[N<<1]; bool pre[M][N];int f[M][N]; #define max(a,b) ((a)>(b)?(a):(b)) #define son (s[i]-'a') void insert(){ now=1; for(int i=0;s[i];i++){ if(!tr[now][son]) tr[now][son]=++cnt; now=tr[now][son]; } tag[now]++; } std::queue<int>q; void acmatch(){ q.push(1); for(int i=0;i<sz;i++) tr[0][i]=1;fail[1]=0; // for(int i=0;i<sz;i++) if(tr[1][i]) fail[tr[1][i]]=1;else tr[1][i]=1; while(!q.empty()){ now=q.front();q.pop(); for(int i=0;i<sz;i++){ if(!tr[now][i]) continue; for(p=fail[now];p&&!tr[p][i];p=fail[p]); fail[tr[now][i]]=p?tr[p][i]:1; q.push(tr[now][i]); } } } //void solve(){ // p=1; // for(int i=0;s[i];i++){ // for(;p&&!tr[p][son];p=fail[p]); // p=p?tr[p][son]:1; // for(int j=p;j;j=fail[j]){ // ans+=tag[j]; //// tag[j]=-1; // } // // -1 break to faster // } // printf("%d\n",ans); //} #undef son int main(){ // freopen("input.txt","r",stdin); scanf("%d%d",&n,&m); for(int i=0;i<n;i++) scanf("%s",s),insert(); acmatch(); pre[0][1]=1; for(int i=1;i<=m;i++){ for(int j=1;j<=cnt;j++){ if(pre[i-1][j]){ for(int k=0;k<sz;k++){ if(!tr[j][k]) tr[j][k]=tr[fail[j]][k]; int &sta=tr[j][k]; pre[i][sta]=1; int val=0; for(int h=sta;h;h=fail[h]) val+=tag[h]; f[i][sta]=max(f[i][sta],f[i-1][j]+val); } } } } // printf("%3d",0); // for(int i=1;i<=cnt;i++) printf("%3d",i);puts(""); // for(int i=1;i<=m;i++){ // printf("%3d",i); // for(int j=1;j<=cnt;j++){ // printf("%3d",f[i][j]); // } // puts(""); // } for(int i=1;i<=cnt;i++) ans=max(ans,f[m][i]); printf("%d\n",ans); return 0; }
满分代码:
#include<queue> #include<stdio.h> #include<memory.h> using std::max; using std::min; typedef long long ll; const int N=103,sz=26; int n,cnt=1,now,p,fail[N],tag[N],tr[N][sz];char s[N<<1]; ll m,ans; #define son (s[i]-'a') void insert(){ now=1; for(int i=0;s[i];i++){ if(!tr[now][son]) tr[now][son]=++cnt; now=tr[now][son]; } tag[now]++; } std::queue<int>q; void acmatch(){ q.push(1);fail[1]=0; for(int i=0;i<sz;i++) tr[0][i]=1; while(!q.empty()){ now=q.front();q.pop(); tag[now]+=tag[fail[now]]; for(int i=0;i<sz;i++){ if(!tr[now][i]) continue; for(p=fail[now];p&&!tr[p][i];p=fail[p]); fail[tr[now][i]]=p?tr[p][i]:1; q.push(tr[now][i]); } } } #undef son struct matrix{ ll s[N][N]; matrix(){ memset(s,-0xf,sizeof s); } }A; matrix operator *(const matrix &a,const matrix &b){ matrix c; for(int i=1;i<=cnt;i++){ for(int j=1;j<=cnt;j++){ for(int k=1;k<=cnt;k++){ c.s[i][j]=max(c.s[i][j],a.s[i][k]+b.s[k][j]); } } } return c; } matrix fpow(matrix a,ll p){ matrix res; for(int i=1;i<=cnt;i++) res.s[i][i]=0; for(;p;p>>=1,a=a*a) if(p&1) res=res*a; return res; } int main(){ scanf("%d%lld",&n,&m); for(int i=0;i<n;i++) scanf("%s",s),insert(); acmatch(); for(int i=1,p;i<=cnt;i++){ for(int j=0;j<sz;j++){ for(p=i;p&&!tr[p][j];p=fail[p]); p=p?tr[p][j]:1; A.s[i][p]=tag[p]; } } A=fpow(A,m); for(int i=1;i<=cnt;i++) ans=max(ans,A.s[1][i]); printf("%lld",ans); return 0; }