字符串学习笔记

KMP

OI-wiki上有一个很不错的kmp做法,就是直接把模式串与文本串用特殊符号链接,然后求前缀数组即可
感觉vector可能更舒服(?)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>

using namespace std;

const int N=2000000;
char a[N],b[N];
vector<char>c;
vector<int>kmp;

void solve(){
	scanf("%s%s",a,b);
	int la=strlen(a),lb=strlen(b);
	for(int i=0;i<lb;++i) c.push_back(b[i]);
	c.push_back('\0');
	for(int i=0;i<la;++i) c.push_back(a[i]);
	kmp.push_back(0);
	for(int i=1;i<=la+lb;++i){
		int j=kmp[i-1];
		while(j>0&&c[i]!=c[j]) j=kmp[j-1];
		if(c[i]==c[j]) kmp.push_back(j+1);
		else kmp.push_back(j);
		if(kmp[i]==lb) printf("%d\n",i-2*lb+1);
	}
	for(int i=0;i<lb;++i) printf("%d ",kmp[i]);
}

int main(){
	solve();
	return 0;
}

Manacher

跟KMP一样,本质都是用已更新状态来减少未知状态的计算

注意边界

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>

using namespace std;

vector<char>a;
vector<int>p;

void init(){
	a.push_back('%');a.push_back('|');
	char c;
	while(scanf("%c",&c)!=EOF){
		a.push_back(c);
		a.push_back('|');
	}
	a.push_back('$');
}

void manacher(){
	init();	p.push_back(1);
	int sz=a.size(),ans=0;
	for(int i=1,mid=0,r=0;i<sz;++i){
		if(i<=r) p.push_back(min(p[mid*2-i],r-i+1));
		else p.push_back(1);
		while(a[i-p[i]]==a[i+p[i]])
			++p[i];
		if(p[i]+i>r) r=p[i]+i-1,mid=i;
		ans=max(ans,p[i]);
	}
	printf("%d\n",ans-1);
}

int main(){
	manacher();
	return 0;
}

回文匹配

KMP+Manacher+二阶前缀和

1.二阶前缀和的数组最前面需要两个0,因为

\[\sum_{i=0}^{n-1}\sum_{j=m/2+1}^{f_i}\sum_{k=i-j+m}^{i+j-1}a_k \]

\[=\sum_{i=0}^{n-1}\sum_{j=m/2+1}^{f_i}(pre_{i+j-1}-pre_{i-j+m-1}) \]

\[=\sum_{i=0}^{n-1}((num_{i+f_i-1}-num_{i+m/2-1})-(num_{i-m/2+m-2}-num_{i-f_i+m-2})) \]

这四项的下届分别相对原来的0进行了-0,-1,-1,-2的处理,所以要流出两个空位置

2.前缀和的\(O(n^2)\)算法中使用循环,不需要考虑下界大于上界的情况,但是二阶前缀和需要!这个问题卡了我一晚上( 任何时候进行前缀和都不要忘了判断上下界的大小。

#include<bits/stdc++.h>
#define ll long long 
using namespace std;

const int N=10000000;
string s1,s2;int n1,n2;
int num[N],f[N];

void KMP(){
	string s3=s2+"$"+s1;int sz=n1+n2+1;
	vector<int> kmp;kmp.clear();kmp.push_back(0);
	for(int i=1;i<sz;++i){
		int j=kmp[i-1];
		while(j>0&&s3[i]!=s3[j]) j=kmp[j-1];
		if(s3[i]==s3[j]) kmp.push_back(j+1);
		else kmp.push_back(j);
		if(kmp[i]==n2) num[i-n2-1]++;
	}
	for(int i=1;i<n1;++i) num[i]+=num[i-1];
	for(int i=1;i<n1;++i) num[i]+=num[i-1];
	for(int i=n1+1;i>=2;--i) num[i]=num[i-2];
}

void Manacher(){
	string s4="$"+s1+"%";
	vector<int> p;p.push_back(1);
	for(int i=1,mid=0,r=0;i<n1+2;++i){
		if(i<=r) p.push_back(min(p[mid*2-i],r-i+1));
		else p.push_back(1);
		while(s4[i-p[i]]==s4[i+p[i]]) ++p[i];
		if(i+p[i]>r){
			r=i+p[i]-1;mid=i;
		}
	}
	for(int i=2;i<=n1+1;++i) f[i]=p[i-1];
}

void solve(){
	unsigned int ans=0;num[0]=num[1]=0;
	// for(int i=1;i<=n1;++i)
	// 	for(int j=n2/2+1;j<=f[i];++j)
	// 		ans+=num[i+j-1]-num[i-j+n2-1];
	for(int i=2;i<=n1+1;++i)
		ans+=num[i+f[i]-1]-num[min(i+f[i]-1,i+n2/2-1)]-num[i-n2/2+n2-2]+num[min(i-n2/2+n2-2,i-f[i]+n2-2)];
	cout<<ans<<endl;
}

int main(){
	cin>>n1>>n2>>s1>>s2;
	KMP();Manacher();solve();
	return 0;
}

Trie

于是他错误的点名开始了

两种建树方法,使用数组模拟和直接使用指针(好像没啥区别),使用vector能有效避免数组不知道开多大的问题

//静态数组
#include<bits/stdc++.h>
#define int long long
using namespace std;

int n,m;
struct TRIE1{
	int cnt;
	int trie[1000000][30];
	int end[1000000];

	void insert(string a){
		int u=0,len=a.length();
		for(int j=0;j<len;++j){
			if(!trie[u][a[j]-'a']) trie[u][a[j]-'a']=++cnt;
			u=trie[u][a[j]-'a'];
		}end[u]++;
	}

	void fnd(string a){
		int u=0,len=a.length();
		for(int j=0;j<len;++j){
			int num=a[j]-'a';
			if(!trie[u][num]){
				printf("WRONG\n");return;
			}u=trie[u][num];
		}if(end[u]==1){
			end[u]++;printf("OK\n");
		}else if(end[u]==0) printf("WRONG\n");
		else printf("REPEAT\n");
	}
}Trie;

signed main(){
	cin>>n;for(int i=1;i<=n;++i){
		string tmp;cin>>tmp;Trie.insert(tmp);
	}cin>>m;for(int i=1;i<=m;++i){
		string tmp;cin>>tmp;Trie.fnd(tmp);
	}
	return 0;
}
//vector
#include<bits/stdc++.h>
#define int long long
using namespace std;

int n,m,cnt;
struct node{
	int son[30],end;
	// int fa;
};vector<node>trie;

void build(string a){
	int u=0,len=a.length();
	for(int i=0;i<len;++i){
		int num=a[i]-'a';
		if(!trie[u].son[num]){
			trie[u].son[num]=++cnt;
			trie.push_back({NULL,0});
		}
		u=trie[u].son[num];
	}
	++trie[u].end;
}

void fnd(string a){
	int u=0,len=a.length();
	for(int i=0;i<len;++i){
		int num=a[i]-'a';
		if(!trie[u].son[num]){cout<<"WRONG"<<endl;return;}
		u=trie[u].son[num];
	}
	if(trie[u].end==1) {++trie[u].end;cout<<"OK"<<endl;}
	else if(trie[u].end==0) cout<<"WRONG"<<endl;
	else cout<<"REPEAT"<<endl;
}

signed main(){
	node tmp={NULL,0};
	trie.push_back({NULL,0});
	cin>>n;for(int i=1;i<=n;++i){string tmp;cin>>tmp;build(tmp);}
	cin>>m;for(int i=1;i<=m;++i){string tmp;cin>>tmp;fnd(tmp);}
	return 0;
}

最长异或路径

posted @ 2024-03-21 12:02  hcx1999  阅读(2)  评论(0编辑  收藏  举报