字符串杂选

\(T1\)

考虑 \(KMP\)\(nxt\) 数组。

发现答案即 \(n-next[n]\) ,输出即可。

$code$
#include <bits/stdc++.h>
#define ll long long
using std::cin; using std::cout; using std::cerr;
const int N=1e6+10;
int nxt[N];
char a[N];
int n;
inline void getnxt(void){
	int j=0;
	for(int i=2;i<=n;++i){
		while(j&&a[i]!=a[j+1]) j=nxt[j];
		if(a[i]==a[j+1]) ++j;
		nxt[i]=j;
	}
}
int main(){
	cin>>n;
	cin>>a+1;
	getnxt();
	cout<<n-nxt[n];
	return 0;
}

\(T2\)

考虑到这道题又要求拼接,而且又出现前缀。所以继续考虑 \(kmp\)

但是这题还要求长度最大周期,也就是剩下的一段最小,所以可以考虑直接求一个 \(nxt'\) 数组。那还是同样的道理。但是你会发现 \(j\) 通常是 \(nxt[i]\) 。所以直接可以推导出一个式子,大概就长成这副模样:

\[nxt[i]=\begin{cases} fail_i & nxt[nxt'[i]]=0 \\ nxt[nxt'[i]] & \text{Otherwise} \end{cases}\]

但是有些时候是不会被计算的,因为这个串并不会存在一个周期,这个时候这个串肯定会 \(nxt'[i] < \left[\dfrac{i}{2}\right]\). 然后这种情况我就不更新 \(ans\) 就好。

因为数据范围高达 \(10^6\) 所以最大的 \(ans\) 也就是 \({10^6} ^2=10^{12} .\) 需要 \(\text{long long}\) 不然会傻乎乎地挂掉。

$code$
#include <bits/stdc++.h>
#define ll long long
#define int long long 
using std::cin; using std::cout; using std::cerr;
const int N=1e6+10;
int nxt[N],fail[N];
char a[N];
int n,ans;
inline void getnxt(void){
	int j=0;
	for(int i=2;i<=n;++i){
		while(j&&a[j+1]!=a[i]) j=fail[j];
		if(a[i]==a[j+1]) ++j;
		fail[i]=j;
	}
	j=0;
	for(int i=2;i<=n;++i){
		j=fail[i];
		nxt[i]=(nxt[j]==0?fail[i]:nxt[j]);
	}
}
signed main(){
	cin>>n>>a+1;
	getnxt();
	for(int i=2;i<=n;++i){
		if(i-nxt[i]>=ceil(i/2.0) && nxt[i])
			ans+=i-nxt[i];
	}
	return cout<<ans<<"\n",0;
}

彬哥甚至可以想出这个题的思路。虽然他好像不是很会写 \(\text{kmp.}\)

\(T3\)

题意:添加最少的字符使整个串回文起来。

那直接计算最长的可以接触到整个串末尾位置就好了。也就是 \(i+r[i]-1 = n.\)

你看这个题目标题也应该知道这题目的做法了吧。 ——周老

$code$
#include <bits/stdc++.h>
#define ll long long
#define f(x) (x<<1|1)
using std::cin;
using std::cout;
const int N=2.2e7+10;
int r[N],a[N],b[N];
int center,limit,ans;
int n;
char ch[N];
int main(){
	cin>>n;
	cin>>(ch+1);
	for(int i=1;i<=n;++i)
		b[i]=ch[i]+1;
	for(int i=1;i<=n;++i)
		a[2*i-1]=200,a[2*i]=b[i];
	a[n<<1|1]=200;
	for(int i=1;i<=(n<<1|1);++i){
		if(i<=limit) r[i]=std::min(limit-i,r[2*center-i]);
		while(i-r[i]>=1&&a[i-r[i]]==a[i+r[i]]) ++r[i];
		if(i+r[i]>limit) center=i,limit=i+r[i];
	}
	for(int i=(n<<1|1);i>=1;--i){
		if(i+r[i]-1==f(n)) ans=f(n)-(f(n)-i)*2-1;
	}
	cout<<ans/2<<'\n';
}

\(T4\)

先用 \(dfs\) 跑一边整棵树的所有节点到根节点的异或和。考虑到一个点 \(u\) 到一个点 \(v\) 的异或和 \(=\) \(u\) 到根节点的异或和 \(\oplus\) \(v\) 到根节点的异或和,所以最大化两个点 \(s[i] \oplus s[j] .\)

然后对于每个 \(s[i]\) 找到一个 \(s[j]\) 使 \(s[i] \oplus s[j]\)在最大化。不显然这个是可以在字典树上面跑的。

先把所以 \(s[i]\) 挂到字典树上面。也就是二进制串挂上去。

贪心。如果字典树上面存在 \(s[i]\) 该位置不同的值那就往那个值身上跑,否则只能去跑相同的值。显然高位的 \(1\) 比所有低位的 \(1\) 叠加起来都要更大,所以贪心成立。

最后去找所有 \(s[i]\) 答案中的最大值就好了,时间复杂度是 \(\mathcal{O(n \log_2\alpha)}\) 被时间所允许。应该没有更加优秀的做法了吧。

细节:要添加前导零。

$code$
// Qx 's data
#include <bits/stdc++.h>
#define int long long
#define fir first
#define sec second
#define add push_back
using std::cin;
using std::cout;
using std::cerr;
const int N=1e6+10,limit=35;
struct Node{
	int num,son[2];
}T[N];
int cnt,n,m;
void Insert(std::vector<int> s){
	int now=0;
	for(int ch:s){
		if(!T[now].son[ch]) T[now].son[ch]=++cnt;
		now=T[now].son[ch], T[now].num++;
	}
}
std::vector<int> Find(std::vector<int> s){
	int now=0;
	std::vector<int> res;
	for(int ch:s){
		if(T[now].son[ch^1]) res.add(1), now=T[now].son[ch^1];
		else res.add(0), now=T[now].son[ch];
	}
	return res;
}
int ans;
int s[N],a[N];
std::vector<std::pair<int,int> > G[N];
void dfs(int u,int f){ for(std::pair<int,int>pr:G[u]) if(pr.fir^f) s[pr.fir]=pr.sec^s[u], dfs(pr.fir,u); }
inline void to2(std::vector<int>& vec,int x){
	int num1=x,ret=0;
	while(x) vec.push_back(x&1), x>>=1; 
	while(num1) ret++,num1>>=1;
	int p=limit-ret;  
	while(p--) vec.add(0);
	reverse(vec.begin(),vec.end());
}
int to10(std::vector<int> vec){
	reverse(vec.begin(),vec.end());
	int answer=0,base=1;
	for(int x:vec) answer+=x*base,base<<=1;
	return answer;
}
signed main(){
	scanf("%lld",&n);
	for(int i=1;i<n;++i){
		int opu,opv,opw;
		scanf("%lld%lld%lld",&opu,&opv,&opw), G[opu].add({opv,opw}), G[opv].add({opu,opw});
	}
	dfs(1,0);
	for(int i=1;i<=n;++i){
		std::vector<int> tmp;
		tmp.clear();
		to2(tmp,s[i]);
		Insert(tmp);
	}
	for(int i=1;i<=n;++i){
		std::vector<int> tmp;
		to2(tmp,s[i]), ans=std::max(ans,to10(Find(tmp)));
	}
	return printf("%lld\n",ans),0;
}

\(T8\) 断环成链然后后缀数组跑一跑就完事。后缀数组的板子还是很简单的,反正我都是 \(\mathcal{O(n \log_2 ^2 n)} .\)

posted @ 2024-01-06 13:51  q(x)  阅读(7)  评论(0编辑  收藏  举报