https://vjudge.net/problem/CodeForces-1138D
题目
有两个字符串s和t,都只包括字符“0”和“1”,要求调整s中字符的顺序,最大化t作为字串出现的次数。
$1 leqslant |s| leqslant 500\,000$,$1 leqslant |s| leqslant 500\,000$
题解
kmp模板题+贪心……
kmp算法得到的是每个位置的最长公共前缀后缀,所以只需要使用整个字符串的最长公共前缀后缀就可以了
先统计s中每个字符的个数,然后照着t贪心填充,如果没有把剩下的都输出,如果把t填充完了那么直接跳到前缀后面继续填充
为什么出现次数最多?因为填充完毕一次以后,最少需要填充“整个字符串长度-前缀长度”个字符,填充其他的肯定没有这个更优
时间复杂度O(n)
AC代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cctype>
#include<cassert>
#define REP(i,a,b) for(register int i=(a); i<(b); i++)
#define REPE(i,a,b) for(register int i=(a); i<=(b); i++)
#define PERE(i,a,b) for(register int i=(a); i>=(b); i--)
using namespace std;
typedef long long ll;
#define MAXN 500007
int f[MAXN];
char s[MAXN], t[MAXN];
inline void getf(char *s, int l) {
int t=f[0]=-1;
REP(i,0,l) {
while(t>=0 && s[i]!=s[t]) t=f[t];
t++; f[i+1]=t;
}
}
int main() {
scanf("%s%s", s, t);
int ls=strlen(s), lt=strlen(t);
int cnt[2]={0,0};
getf(t,lt);
REP(i,0,ls) cnt[s[i]-'0']++;
int pos=0;
while(1) {
if(cnt[t[pos]-'0']>0) {
putchar(t[pos]);
cnt[t[pos]-'0']--;
pos++;
if(pos==lt) pos=f[pos];
} else break;
}
while(0<cnt[0]--) putchar('0');
while(0<cnt[1]--) putchar('1');
putchar('
');
}