codeforces1137B kmp(fail的妙用)
题意:给出$s$和$t$两个串,让你构造出一个答案串,使得答案串中的01数量和s一样,并且使$t$在答案串中作为子串出现次数最多。
思路:
要想出现的次数尽可能多,那么就要重复的利用,哪一部分是可以重复利用的呢?就是前缀和后缀相同的部分,然后我们就想到了$kmp$算法中$fail$函数就是求这个东西的,那么我们先对t串fail一遍得到$next$数组,然后先使前缀出现一次,然后就使除了前缀以外的后缀尽可能出现的多,这样得到的答案串必定是最多的,最后把剩余的01输出就可以了。
记住扫描字符串的for循环千万不要用(i<strlen(s))当条件,否则就是个$n2$的算法了(你会tle15)。
#include<bits/stdc++.h> #define clr(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; const int maxn=500010; int n,f[maxn],a,b; char s[maxn],p[maxn]; void fail(){ f[0]=-1; for(int j=1;j<n;j++) { for(int i=f[j-1];;i=f[i]) { if(p[j]==p[i+1]){ f[j]=i+1; break; }else if(i==-1) { f[j]=-1; break; } } } for(int i=0;i<n;i++)f[i]++; } int main(){ while(scanf("%s",s)!=EOF) { scanf("%s",p); n=strlen(p); fail(); a=b=0; int si=strlen(s); for(int i=0;i<si;i++) { if(s[i]=='0')a++; else b++; } int xx=0,yy=0; for(int i=0;i<f[n-1];i++) { if(p[i]=='0')xx++; else yy++; } if(a>=xx&&b>=yy) { for(int i=0;i<f[n-1];i++)printf("%c",p[i]); a-=xx,b-=yy; int xxx=0,yyy=0; for(int i=f[n-1];i<n;i++) { if(p[i]=='0')xxx++; else yyy++; } int flag=0; while(a>0&&b>0) { if(a<xxx||b<yyy)break; for(int i=f[n-1];i<n;i++) { printf("%c",p[i]); } a-=xxx,b-=yyy; } while(a>0){ printf("0"); a--; } while(b>0){ printf("1"); b--; } puts(""); }else{ printf("%s\n",s); } } }
——愿为泰山而不骄
qq850874665~~