题解 简单的字符串

传送门

毒瘤题

相关略掉的证明见pdf,我并没有全看完,待补吧

首先这个题有暴力hash和最小表示法的做法
然后正解,有两种做法

  • 对于循环同构串,可以定义一种很套路的变换 \(C(x,y)=x[1]y[n]x[2]y[n-1]..x[n]y[1]\)
    那就有 \(C(x,x)=x[1]x[n]x[2]x[n-1]…x[n]x[1]\) 为一个偶数长度的回文串
    如果将一个循环同构串写成 \(uvvu\) 的形式,再令 \(x=uv, y=vu\),就有 \(C(x,y)=C(u,u)C(v,v)\),是由两个偶数长度的回文串拼接而成
    则一个包含循环同构串的串 \(auvvub\),变换后就是 \(C(a,b)C(u,u)C(v,v)\)(要求 \(a, b\) 长度相等)
    利用这一性质,可以将在一个串中找循环同构串的任务转化为找偶双回文后缀的任务
    引理:如果 \(s\) 是一个双回文串, 则存在一种回文拆分 \(s=ab\),使得 \(a\)\(s\) 的最长回文前缀,或者 \(b\)\(s\) 的最长回文后缀
    于是可以求出每个后缀的最长回文前缀和最长回文后缀,剩下的部分hash判等
    最长回文后缀可以靠hash预处理全求出来
    每个后缀的最长回文前缀需要manacher或回文树

然后这题还有一个不需要太多前置知识的做法:
先引理:若 \(S\)\(T\) 循环同构,那么 \(S=uv,T=vu\),那么 \(u\)\(v\) 至少有一个是 \(S\)\(T\)最长匹配的前后缀,即 \(u\)\(ST\) 的最长的 \(\leqslant\) 一半长的border或者 \(v\)\(TS\) 最长的 \(\leqslant\) 一半长的border
证明的话感谢这里的大神证明
大意是(以 \(ST\) 为例)若还有一个更长的border \(h\),那 \(h-u\)\(u\) 的周期,同时 \(h-u\) 也是 \(v\) 的border
于是 \(uv\) 就完全是由 \(h-u\) 组成了,可以有其它的划分方法
于是分别check两种情况
第二种情况可以枚举中点和串长,向两边hash判断匹配的最长长度,即为最长border
第一种情况的话可以枚举左端点,对每个右端点KMP求border
但需要跳nxt数组找 \(\leqslant\) 一半长的border,根据【证明略了】,border数量是log级别的,但多个log会T
引理:一个串 \(\geqslant \frac{len}{2}\) 的border长度构成一个等差数列,证明略了
于是可以算出公差,直接计算出应该跳到哪
剩下的部分hash判等即可

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

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n;
int a[N];

namespace force{
	int s[N], t[N<<1], ans;
	ull p[N<<1], sh[N], th[N<<1];
	const ull base=13131;
	inline ull hashing(int l, int r) {return th[r]-th[l-1]*p[r-l+1];}
	void solve() {
		p[0]=1;
		int lim=n<<1;
		for (int i=1; i<=lim; ++i) p[i]=p[i-1]*base;
		for (int i=1; i<=n; ++i) 
			for (int j=i+1; j<=n; ++j) if (!((j-i+1)&1)) {
				int len=(j-i+1)/2;
				for (int k=i; k<i+len; ++k) s[k-i+1]=a[k];
				for (int k=i+len; k<=j; ++k) t[k-(i+len)+1]=a[k];
				for (int k=1; k<=len; ++k) t[k+len]=t[k];
				for (int k=1; k<=len; ++k) sh[k]=sh[k-1]*base+s[k];
				for (int k=1; k<=len*2; ++k) th[k]=th[k-1]*base+t[k];
				for (int k=1; k<=len; ++k) if (sh[len]==hashing(k, k+len-1)) {
					// cout<<"gets: "<<sh[len]<<' '<<hashing(k, k+len-1)<<endl;
					// cout<<"get: "; for (int i=1; i<=len; ++i) cout<<s[i]<<' '; for (int i=1; i<=len; ++i) cout<<t[i]<<' '; cout<<endl;
					++ans; break;
				}
			}
		printf("%d\n", ans);
		exit(0);
	}
}

namespace task{
	int bdr[N][N], nxt[N], ans;
	const ull base=13131;
	ull h[N], p[N];
	ull hashing(int l, int r) {return h[r]-h[l-1]*p[r-l+1];}
	void solve() {
		p[0]=1;
		for (int i=1; i<=n; ++i) p[i]=p[i-1]*base;
		for (int i=1; i<=n; ++i) h[i]=h[i-1]*base+a[i];
		for (int i=1; i<=n; ++i) {
			int lst=0;
			for (int j=1; i-j+1>0&&i+j<=n; ++j) {
				if (hashing(i-j+1, i)==hashing(i+1, i+j)) lst=j;
				bdr[i-j+1][i+j]=lst;
			}
		}
		for (int i=1; i<=n; ++i) {
			// cout<<"i: "<<i<<endl;
			nxt[1]=0;
			for (int j=2,k=0; i+j-1<=n; ++j) {
				while (k && a[i+j-1]!=a[i+k]) k=nxt[k];
				if (a[i+j-1]==a[i+k]) ++k;
				nxt[j]=k;
			}
			for (int j=1; i+j-1<=n; ++j) if (!(j&1)) {
				// cout<<"j: "<<j<<endl;
				int len=nxt[j];
				// while (len>j/2) len=nxt[len];
				if (len>j/2) {
					int dlt=j-nxt[j];
					len=j-dlt*((j/2)/dlt);
					len=nxt[len];
				}
				int mid=i+(j/2)-1;
				// if (i==5 && j==10) cout<<"len: "<<i<<' '<<j<<' '<<len<<endl;
				// cout<<"hash: "<<i+len<<' '<<mid<<' '<<mid+1<<' '<<i+j-len-1<<endl;
				// cout<<"val: "<<hashing(i+len, mid)<<' '<<hashing(mid+1, i+j-len-1)<<endl;
				if (hashing(i+len, mid)==hashing(mid+1, i+j-len-1)) {
					++ans;
					// cout<<"add1: "<<i<<' '<<i+j-1<<endl;
				}
				else {
					int tem=bdr[i][i+j-1];
					// if (i==5 && j==10) cout<<"bdr: "<<i<<' '<<i+j-1<<' '<<tem<<endl;
					// if (i==5 && j==10) cout<<"mid: "<<mid<<endl;
					if (hashing(i, mid-tem)==hashing(mid+tem+1, i+j-1)) {
						++ans;
						// cout<<"add2: "<<i<<' '<<i+j-1<<endl;
					}
				}
			}
		}
		printf("%d\n", ans);
		exit(0);
	}
}

signed main()
{
	freopen("s.in", "r", stdin);
	freopen("s.out", "w", stdout);

	n=read();
	for (int i=1; i<=n; ++i) a[i]=read();
	// force::solve();
	task::solve();
	
	return 0;
}
posted @ 2021-10-04 21:31  Administrator-09  阅读(7)  评论(0编辑  收藏  举报