题解 字符消除2

传送门

首先证一个结论:t集合即是原串的boarder不用证了吧

于是转化题意:要构造一个boarder集合确定的01串
考虑从小的boarder推向大的boarder,令小boarder为s,大boarder为t

  1. \(|s|*2 \leqslant |t|\) 此时在s后面补上s的一段后缀即可
  2. \(|s|*2 > |t|\) 把原串复制一遍接在最后,其它位置全补0
    kmp check一下它有没有多出来新的boarder,如果有就把补的最后一个0换成1

证明见这里

Code:

#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 200010
#define ll long long
#define ull unsigned long long
//#define int long long 

int n;
char s[N];
ull h[N], p[N];
const ull base=131;
inline ull hashing(ull* h, int l, int r) {return h[r]-h[l-1]*p[r-l+1];}

namespace force{
	int t[2000], top;
	ull h2[2010];
	void solve() {
		//cout<<"solve "<<n<<endl;
		top=0;
		for (int i=1; i<=n; ++i) h[i]=h[i-1]*base+s[i];
		for (int i=1; i<=n; ++i) {
			ull m=hashing(h, 1, i);
			int j;
			for (j=i+1; j+i-1<=n; j+=i) 
				if (hashing(h, j, j+i-1)!=m) goto jump;
			if (j<=n && hashing(h, 1, n-j+1)!=hashing(h, j, n)) goto jump;
			t[++top]=i;
			jump: ;
		}
		//cout<<"top: "<<top<<endl;
		//for (int i=1; i<=top; ++i) cout<<t[i]<<' '; cout<<endl;
		int lim=1<<n;
		for (int s=0; s<lim; ++s) {
			//cout<<"now s="<<bitset<6>(s)<<endl;
			int pos=1;
			for (int i=1; i<=n; ++i) h2[i]=h2[i-1]*base+(s&(1<<(i-1))?1:0);
			for (int i=1; i<=n; ++i) {
				ull m=hashing(h2, 1, i);
				int j;
				for (j=i+1; j+i-1<=n; j+=i) 
					if (hashing(h2, j, j+i-1)!=m) goto jump2;
				if (j<=n && hashing(h2, 1, n-j+1)!=hashing(h2, j, n)) goto jump2;
				//cout<<"cmp: "<<i<<' '<<t[pos]<<endl;
				if (i==t[pos]) ++pos;
				else break;
				jump2: ;
			}
			if (pos==top+1) {
				for (int i=n-1; ~i; --i) printf("%d", s&(1<<i)?1:0); printf("\n");
				break;
			}
		}
	}
}

namespace task{
	int nxt[N], t[N], top, siz;
	char ans[N];
	void solve() {
		top=0;
		nxt[1]=0;
		for (int i=2,j=0; i<=n; ++i) {
			while (j && s[i]!=s[j+1]) j=nxt[j];
			if (s[i]==s[j+1]) ++j;
			nxt[i]=j;
		}
		int it=n;
		do {t[++top]=it; it=nxt[it];} while (it) ;
		//cout<<"nxt: "; for (int i=1; i<=top; ++i) cout<<t[i]<<' '; cout<<endl;
		siz=0;
		if (t[top]==1) ans[++siz]=0;
		else {
			while (t[top]>1) ans[++siz]=0, --t[top];
			ans[++siz]=1;
		}
		for (int i=top-1; i; --i) {
			//cout<<"i: "<<i<<' '<<siz<<endl;
			//cout<<"ans: "; for (int i=1; i<=siz; ++i) printf("%d", ans[i]); printf("\n");
			if (siz*2>=t[i]) 
				for (int j=siz-(t[i]-siz)+1,ends=siz; j<=ends; ++j) ans[++siz]=ans[j];
			else {
				//cout<<"pos1"<<endl;
				int dlt=t[i]-siz+1;
				for (int j=siz+1; j<dlt; ++j) ans[j]=0;
				for (int j=dlt; j<=t[i]; ++j) ans[j]=ans[j-dlt+1];
				nxt[1]=0;
				for (int k=2,j=0; k<=t[i]; ++k) {
					while (j && ans[k]!=ans[j+1]) j=nxt[j];
					if (ans[k]==ans[j+1]) ++j;
					nxt[k]=j;
				}
				if (nxt[t[i]]!=siz) ans[dlt-1]=1;
				siz=t[i];
			}
		}
		for (int i=1; i<=siz; ++i) printf("%d", ans[i]); printf("\n");
	}
}

signed main()
{
	int T;
	
	//cout<<double(sizeof(h)*3)/1024/1024<<endl;
	p[0]=1;
	for (int i=1; i<=2000; ++i) p[i]=p[i-1]*base;
	scanf("%d", &T);
	while (T--) {
		scanf("%s", s+1);
		n=strlen(s+1);
		//force::solve();
		task::solve();
	}
	
	return 0;
}
上一篇:洛谷 P4146 序列终结者


下一篇:[SDOI2008] 洞穴勘测