字符串杂选

T1

考虑 KMPnxt 数组。

发现答案即 nnext[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]={failinxt[nxt[i]]=0nxt[nxt[i]]Otherwise

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

因为数据范围高达 106 所以最大的 ans 也就是 1062=1012. 需要 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;
}

彬哥甚至可以想出这个题的思路。虽然他好像不是很会写 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 到根节点的异或和 v 到根节点的异或和,所以最大化两个点 s[i]s[j].

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

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

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

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

细节:要添加前导零。

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 断环成链然后后缀数组跑一跑就完事。后缀数组的板子还是很简单的,反正我都是 O(nlog22n).

posted @   q(x)  阅读(11)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示