概述
写在前面
“模板库”这一系列文章用来复习
O
I
OI
OI模板
由于时间原因,作者无法一一亲自调试其中的程序,也因如此,有一部分程序来自于互联网,如果您觉得这侵犯了您的合法权益,请联系
(
Q
Q
2068926345
)
(QQ2068926345)
(QQ2068926345)删除。
对于给您造成的不便和困扰,我表示深深的歉意。
本系列文章仅用于学习,禁止任何人或组织用于商业用途。
本系列文章中,标记*的为选学算法,在
N
O
I
P
NOIP
NOIP中较少涉及。
字符串算法
KMP匹配算法
【简介】
K
M
P
KMP
KMP算法是一种改进的字符串匹配算法,由
D
.
E
.
K
n
u
t
h
,
J
.
H
.
M
o
r
r
i
s
D.E.Knuth,J.H.Morris
D.E.Knuth,J.H.Morris和
V
.
R
.
P
r
a
t
t
V.R.Pratt
V.R.Pratt同时发现,因此人们称它为克努特——莫里斯——普拉特操作(简称
K
M
P
KMP
KMP算法)。
K
M
P
KMP
KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。
具体实现就是实现一个
n
e
x
t
(
)
next()
next()函数,函数本身包含了模式串的局部匹配信息。时间复杂度
Θ
(
m
+
n
)
Theta(m+n)
Θ(m+n)。
【代码实现】
//problemID:https://www.luogu.org/problemnew/show/P3375
#include<cstdio>
#include<cstring>
char s[1000007],s1[1000007];
int next[1000007];
int len,len1,ans;
void get_next(){
int j=0;
for(int i=2;i<=len1;++i){
while(j && s1[i]!=s1[j+1])j=next[j];
if(s1[i]==s1[j+1])++j;
next[i]=j;
}
}
void kmp(){
int j=0;
for(int i=1;i<=len;++i){
while(j && s[i]!=s1[j+1])j=next[j];
if(s[i]==s1[j+1])++j;
if(j==len1)printf("%dn",i-j+1),j=next[j];
}
for(int i=1;i<=len1;++i)printf("%d ",next[i]);
}
int main(){
scanf("%s%s",s+1,s1+1);
len=strlen(s+1);
len1=strlen(s1+1);
get_next();
kmp();
return 0;
}
复杂度 Θ ( m + n ) Theta(m+n) Θ(m+n)
Manacher算法
【简介】
M a n a c h a r Manachar Manachar算法主要是处理字符串中关于回文串的问题的,它可以在 Θ ( n ) Theta(n) Θ(n)的时间处理出以字符串中每一个字符为中心的回文串半径,由于将原字符串处理成两倍长度的新串,在每两个字符之间加入一个特定的特殊字符,因此原本长度为偶数的回文串就成了以中间特殊字符为中心的奇数长度的回文串了。
【代码实现】
//problemID:https://www.luogu.org/problemnew/show/P3805
#include<iostream>
#include<cstdio>
#include<ctype.h>
#include<cstring>
using namespace std;
int p[22000007];
char s[22000007],ss[11000007];
void manacher(int l){
int ans=0,mid=0,mx=-1;
for(int i=0;i<=l;++i){
if(mid+mx>i)p[i]=min(p[(mid<<1)-i],mid+mx-i);
while(i-p[i]-1>=0 && s[i-p[i]-1]==s[i+p[i]+1])p[i]++;
if(mid+mx<i+p[i])mid=i,mx=p[i];
ans=max(ans,p[i]);
}
printf("%d",ans);
}
int main(){
scanf("%s",ss);
int len=strlen(ss),l=-1;
for(int i=0;i<len;i++) s[++l]='#',s[++l]=ss[i];
s[++l]='#';
manacher(l);
return 0;
}
复杂度 Θ ( n ) Theta(n) Θ(n)
字符串hash
【简介】
H a s h Hash Hash,有时译做“散列”,也有直接音译为“哈希”的,就是把任意长度的输入(又叫做预映射 p r e − i m a g e pre-image pre−image)通过散列算法变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来确定唯一的输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。
【代码实现】
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef unsigned long long ull;
ull base=137;
struct data{
ull x,y;
bool operator <(data b)const{return x<b.x;}
}a[10007];
char s[10007];
int n,ans=1;
ull mod1=19260817;
ull mod2=19660813;
ull hash1(char s[]){
int len=strlen(s);
ull ans=0;
for(int i=0;i<len;++i)ans=(ans*base+(ull)s[i])%mod1;
return ans;
}
ull hash2(char s[]){
int len=strlen(s);
ull ans=0;
for(int i=0;i<len;i++)ans=(ans*base+(ull)s[i])%mod2;
return ans;
}
signed main(){
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%s",s);
a[i].x=hash1(s);
a[i].y=hash2(s);
}
sort(a+1,a+n+1);
for(int i=2;i<=n;++i)
if(a[i].x!=a[i-1].x || a[i-1].y!=a[i].y)ans++;
printf("%dn",ans);
return 0;
}
Trie树
【简介】
又称单词查找树, T r i e Trie Trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。
【代码实现】
#include<cstring>
#include<iostream>
#include<conio.h>
using namespace std;
namespace trie
{
template<classT,size_t CHILD_MAX>
/*
ParameterT:Typeofreserveddata.
ParameterCHILD_MAX:Sizeofarryofpointerstochildnode.
*/
struct nod
{
Treserved;
nod<T,CHILD_MAX>*child[CHILD_MAX];
nod()
{
memset(this,0,sizeof*this);
}
~nod()
{
for(unsignedi=0; i<CHILD_MAX; i++)
deletechild[i];
}
void Traversal(char*str,unsignedindex)
{
unsignedi;
for(i=0; i<index; i++)
cout<<str[i];
cout<<'t'<<reserved<<endl;
for(i=0; i<CHILD_MAX; i++)
{
if(child[i])
{
str[index]=i;
child[i]->Traversal(str,index+1);
}
}
return;
}
};
template<classT,size_t CHILD_MAX=127>
/*
ParameterT:Typeofreserveddata.
ParameterCHILD_MAX:Sizeofarryofpointerstochildnode.
*/
classtrie
{
private:
nod<T,CHILD_MAX>root;
public:
nod<T,CHILD_MAX>*AddStr(char*str);
nod<T,CHILD_MAX>*FindStr(char*str);
boolDeleteStr(char*str);
void Traversal()
{
char str[100];
root.Traversal(str,0);
}
};
template<classT,size_tCHILD_MAX>
nod<T,CHILD_MAX>*trie<T,CHILD_MAX>::AddStr(char*str)
{
nod<T,CHILD_MAX>*now=&root;
do
{
if(now->child[*str]==NULL)
now->child[*str]=newnod<T,CHILD_MAX>;
now=now->child[*str];
}
while(*(++str)!='