[题解] P5446 [THUPC2018]绿绿和串串

[题解] P5446 [THUPC2018]绿绿和串串

这里有一个不用 manacher 的题解!

不一定更好的阅读体验

字符串哈希

手动模拟一下初始串 \(R\) 变为 \(R'\) 的过程,可以发现是这样递归进行的:对于当前过程中的串,以结尾作为对称轴翻转前 \(|R|-1\) 个字符。

因为要求是 \(S\),是 \(R'\) 的一个前缀,所以最后的 \(R'\) 保留前 \(|S|\) 位和 \(S\) 相等就是一个合法的 \(R\)

本题的核心解法

不管对称变换几次,每一次都是要对一个前缀进行翻折,所以说,如果一个字符串是一个合法的 \(R\),那么这个字符串也是 \(S\) 的一个前缀,这是不难发现的。

更重要的是,一个 \(S\) 的前缀 \(S_{1,i}\) 合法的必要条件是:\(i\) 是一个回文半径为 \(i-1\) 的字符串的回文中心

比如说对于 \(qwqwq\) 这个字符串,位置 \(2,3\) 都是满足上述条件的。

而对于这个回文串的右端点,可以作为一个新的对称轴进行前 \(2i-2\) 个字符的又一次翻折。

这样就可以用并查集维护序列,对于每一次翻折形成的一个新的回文串的右端点 \(r\),把它们用并查集并起来。

那么这样向右翻折到什么时候才能判断充分性呢?

对于当前的一次翻折,新的回文串已经覆盖掉了 \(S\) 的所有字符,往前推回去,这个回文中心就是合法的。

比如对于 \(qwqwq\) 的第一个 \(w\),翻折一次形成了 \(qwq\),再翻折一次形成了 \(qwqwq\),那第 \(2\) 个位置就是一个合法的回文中心,而 \(qaqaqqq\) 的第一个 \(a\) 就是非法的。

不难发现,如果右端点 \(r\) 已经翻折到了 \(\geq \lfloor\frac{n}{2}+1\rfloor\) 的位置,那么如果是一个回文中心,往前推之前翻折到这里的回文中心就可以了,而并查集就起到了这样的作用。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
template <typename T>
inline T read(){
	T x=0;char ch=getchar();bool fl=false;
	while(!isdigit(ch)){if(ch=='-')fl=true;ch=getchar();}
	while(isdigit(ch)){
		x=(x<<3)+(x<<1)+(ch^48);ch=getchar();
	}
	return fl?-x:x;
}
#include <vector>
#define ull unsigned long long
const ull P = 13331;
const int maxn = 1e6 + 10;
int T,fa[maxn];
bool vis[maxn];
char s[maxn];
inline int find(int x){
	while(x!=fa[x])x=fa[x]=fa[fa[x]];
	return x;
}
ull Hash1[maxn],Hash2[maxn],power[maxn];
inline ull get1(int l,int r){
	return Hash1[r]-Hash1[l-1]*power[r-l+1];
}
inline ull get2(int l,int r){
	return Hash2[l]-Hash2[r+1]*power[r-l+1];
}
inline void merge(int x,int y){
	x=find(x);y=find(y);
	if(x!=y)fa[x]=y;
}
#define Pair pair<int,int>
#define mp make_pair
vector<Pair> tmp;
int main(){
	T=read<int>();
	while(T--){
		scanf("%s",s+1);int n=strlen(s+1);
		tmp.clear();
		power[0]=1;
		for(int i=1;i<=n;i++){
			vis[i]=false;fa[i]=i;
			Hash1[i]=Hash1[i-1]*P+s[i]-'a'+1;
			power[i]=power[i-1]*P;
		}
		for(int i=n;i>=1;i--){
			Hash2[i]=Hash2[i+1]*P+s[i]-'a'+1;
		}
		int n1=n/2+1;
		for(int i=1;i<=n;i++){
			int len;
			if(i<n1)len=i;
			else len=n-i+1;
			if(get1(i-len+1,i)==get2(i,i+len-1))vis[i]=true,tmp.push_back(mp(i,len));
		}
		for(auto p:tmp){
			int pos=p.first,len=p.second;
			if(vis[pos+len-1])merge(pos,pos+len-1);
		}
		for(int i=1;i<=n;i++){
			int x=find(i);
			if(x>=n1 && vis[x])printf("%d ",i);
		}
		puts("");
	}
	return 0;
}
posted @ 2021-09-03 15:36  ¶凉笙  阅读(96)  评论(0编辑  收藏  举报