2.7 2.8

放寒假了啊~

题解写起来比较麻烦,遂只记录一些算法和 trick。

目前文章格式已经被洛谷题解规范重塑过了

一

用 Trie 存一些数字的二进制,通过交换左右子树的方式,我们可以维护:

1.所有数字+1,2.求所有数字的异或值(当然在能 Trie 上维护的信息均可)

二

ACAM,SA,SAM,后缀排序,后缀排序求 height 数组(神奇性质)

放一些板板

ACAM
#include<bits/stdc++.h>
#define fst first
#define sec second
#define mkp(a,b) make_pair(a,b)
#define usetime() (double)clock () / CLOCKS_PER_SEC * 1000.0
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
const int maxn=2e5+5,maxm=2e6+5;
void read(int& x){
	char c;
	bool f=0;
	while((c=getchar())<48) f|=(c==45);
	x=c-48;
	while((c=getchar())>47) x=(x<<3)+(x<<1)+c-48;
	x=(f ? -x : x);
}
int n;
char c[maxm];
struct node{
	int s[26],fail,ans,in;
};
node t[maxn];
int ans[maxn];
int tot=1;
int insert(){
	int s=strlen(c);
	int p=1;
	for(int i=0;i<s;i++){
		if(!t[p].s[c[i]-'a']) t[p].s[c[i]-'a']=++tot;
		p=t[p].s[c[i]-'a'];
	}
	return p;
}
void build(){
	for(int i=0;i<26;i++) t[0].s[i]=1;
	queue<int> q;
	q.push(1);
	while(!q.empty()){
		int u=q.front();
		q.pop();
		for(int i=0;i<26;i++){
			if(t[u].s[i]){
				t[t[u].s[i]].fail=t[t[u].fail].s[i];
				++t[t[t[u].fail].s[i]].in;
				q.push(t[u].s[i]);
			}
			else t[u].s[i]=t[t[u].fail].s[i];
		}
	}
}
void query(){
	int p=1,s=strlen(c);
	for(int i=0;i<s;i++){
		p=t[p].s[c[i]-'a'];
		++t[p].ans;
	}
}
void topo(){
	queue<int> st;
	for(int i=0;i<=tot;i++){
		if(!t[i].in) st.push(i);
	}
	while(!st.empty()){
		int u=st.front();
		st.pop();
		int v=t[u].fail;
		--t[v].in,t[v].ans+=t[u].ans;
		if(!t[v].in) st.push(v);
	}
}
int que[maxn];
int main(){
	read(n);
	for(int i=1;i<=n;i++){
		scanf("%s",c);
		que[i]=insert();
	}
	build();
	scanf("%s",c);
	query(); topo();
	for(int i=1;i<=n;i++){
		printf("%d\n",t[que[i]].ans);
	}
	return 0;
}
//^o^
SAM(也可用于求 SA)
#include<bits/stdc++.h>
#define fst first
#define sec second
#define mkp(a,b) make_pair(a,b)
#define usetime() (double)clock () / CLOCKS_PER_SEC * 1000.0
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
const int maxn=2e6+5;
void read(int& x){
	char c;
	bool f=0;
	while((c=getchar())<48) f|=(c==45);
	x=c-48;
	while((c=getchar())>47) x=(x<<3)+(x<<1)+c-48;
	x=(f ? -x : x);
}
struct node{
	int s[26],len,fa;
}t[maxn<<1];
int las=1,tot=1;
void add(int c){
	int p=las,np=las=++tot;
	t[np].len=t[p].len+1;
	for(;p&&(!t[p].s[c]);p=t[p].fa) t[p].s[c]=np;
	if(!p) t[np].fa=1;
	else{
		int q=t[p].s[c];
		if(t[q].len==t[p].len+1) t[np].fa=q;
		else{
			int nq=++tot;
			t[nq]=t[q],t[nq].len=t[p].len+1;
			t[np].fa=t[q].fa=nq;
			for(;p&&t[p].s[c]==q;p=t[p].fa) t[p].s[c]=nq;
		}
	}
}
bool check(string& s){
	int p=1;
	for(int i=0;i<(int)s.size();i++){
		if(!t[p].s[s[i]-'a']) return false;
		p=t[p].s[s[i]-'a'];
	}
	return true;
}
string s;
int main(){
	cin>>s;
	for(int i=0;i<(int)s.size();i++){
		add(s[i]-'a');
	}
	while(true){
		cin>>s;
		if(check(s)) cout<<"yes"<<endl;
		else cout<<"no"<<endl;
	}
	return 0;
}
//^o^
后缀排序及 height
#include<bits/stdc++.h>
#define fst first
#define sec second
#define mkp(a,b) make_pair(a,b)
#define usetime() (double)clock () / CLOCKS_PER_SEC * 1000.0
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
const int maxn=2e6+5;
void read(int& x){
	char c;
	bool f=0;
	while((c=getchar())<48) f|=(c==45);
	x=c-48;
	while((c=getchar())>47) x=(x<<3)+(x<<1)+c-48;
	x=(f ? -x : x);
}
int rk[maxn],rki[maxn],sa[maxn],cnt[maxn],id[maxn],h[maxn];
void getSa(string s){
	int n=s.size(),m=150,p=0;
	for(int i=0;i<n;i++) ++cnt[rk[i+1]=s[i]];
	for(int i=1;i<=m;i++) cnt[i]+=cnt[i-1];
	for(int i=n;i>=1;i--) sa[cnt[rk[i]]--]=i;
	for(int w=1;w<=n;w<<=1,m=p){
		int cur=0;
		for(int i=n-w+1;i<=n;i++) id[++cur]=i;
		for(int i=n;i>=1;i--){
			if(sa[i]>w) id[++cur]=sa[i]-w;
		}
		fill(cnt,cnt+m+2,0);
		for(int i=1;i<=n;i++) ++cnt[rk[i]];
		for(int i=1;i<=m;i++) cnt[i]+=cnt[i-1];
		for(int i=n;i>=1;i--) sa[cnt[rk[id[i]]]--]=id[i];
		copy(rk+1,rk+n+1,rki+1);
		p=0;
		for(int i=1;i<=n;i++){
			if(rki[sa[i]]==rki[sa[i-1]]&&rki[sa[i]+w]==rki[sa[i-1]+w]){
				rk[sa[i]]=p;
			}
			else{
				rk[sa[i]]=++p;
			}
		}
		if(p==n) break;
	}
	int k=0;
	for(int i=1;i<=n;i++){
		if(rk[i]==1) continue;
		if(k) --k;
		while(i-1+k<n&&s[i-1+k]==s[sa[rk[i]-1]-1+k]) ++k;
		h[rk[i]]=k;
	}
	for(int i=1;i<=n;i++) cout<<sa[i]<<' ';
	cout<<endl;
	for(int i=1;i<=n;i++) cout<<h[i]<<' ';
	cout<<endl;
}
int main(){
	string s; cin>>s;
	getSa(s);
	return 0;
}
//^o^

三

今日 ARC 又遇见拆位,总之看见位运算拆拆位总是没错的。

2.23 补充一下,看到异或还要想到 Trie 树!

posted @ 2026-02-08 23:45  huangems  阅读(17)  评论(0)    收藏  举报