KMP,HASH,Trie,AC自动机
我做个总结算了下午看了一下AC自动机和学习我的大生物(当然是多谢鑫神了)。。完了要崩。。
1 KMP 只要是学过的人都觉得比较简单吧 但是学不会的人就感觉很难了,我是那种顿悟的然后感觉非常简单的人过程需要自己来体会言传不如身教用身心去体会这个过程就可以成功了。
一种快速匹配子串的东西。具体来说 证明我就不在证了先对自我进行匹配求出nex数组然后和文本串进行匹配考虑这样当我们匹配刚好失配的时候此时还有价值的匹配只能是前面的前缀和当前的东西匹配上了这样才会有价值的匹配不然前面的串往后移动一个距离还是会失配,因为到达改点的时候还是会失配。这样我们每次都利用有价值的匹配实现时间复杂度为O(n);
#include<bits/stdc++.h>
#include<iostream>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<iomanip>
#include<algorithm>
#include<queue>
#include<vector>
#include<stack>
#include<cstdio>
#include<map>
#include<deque>
#include<set>
using namespace std;
inline int read()
{
int x=,f=;char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getchar();}
return x*f;
}
const int maxn=;
char a[maxn],b[maxn];
int nex[maxn],len,tot;
void KMP()
{
int j=;
nex[]=;
for(int i=;i<=tot;i++)
{
while(j&&b[j+]!=b[i])j=nex[j];
if(b[j+]==b[i])j++;
nex[i]=j;
}
j=;
for(int i=;i<=len;i++)
{
while(j&&b[j+]!=a[i])j=nex[j];
if(a[i]==b[j+])j++;
if(j==tot)
{
printf("%d\n",i-j+);
j=nex[j];
}
}
}
int main()
{
//freopen("1.in","r",stdin);
scanf("%s",a+);scanf("%s",b+);
len=strlen(a+);tot=strlen(b+);
KMP();
for(int i=;i<=tot;i++)printf("%d ",nex[i]);
return ;
}
具体就是这样了真的不清楚的话就自己晚上睡觉前思考一下,体会一下这美妙的过程。。
当然我才知道有扩展KMP开个坑,以后填!
扩展KMP:
2 HASH hash可能是初学者最容易理解的算法了我觉得,因为在一定的程度上它表现出来的是暴力的美学也是干净利索的做法。和字符串联系在一起自然是要进行字符串的hash。字符串的hash 的话我们采用字符赋值或者直接使用ASCll 也行但是这显然很容易就冲突了abc bca 直接GG 这里我们日常的hash是直接采用进制hash来搞一些操作。我们试想 a*p^3+b*p^2+c*p 这样和b*p^3+c*p^2+a*p 这样冲突的几率会非常的小或者说在一定的意义下是不可能冲突的因为这就不是一个hash了变成了一个p进制的数字对于这样的数字每个字符串都只能对应一个数字又何谈来冲突呢?数字太大存不下啊map!这个我还不如直接mapstring呢!也对哦那只能取模了模数大一点减少冲突几率然后操作是祷告自己欧皇附身取得模数没有冲突!挂链法好像不太好用嘞。。那我也没办法了。我们有h[i]数组表示前i个字符的hash值然后我们可以求出来任何一段的hash值只要不冲突我们这个就可以实现O(1)判断两个字符串是否相同了!!!这样的话写字符串的匹配应该也是O(1)的。本来是要表演一下hash 的但是无奈map还是比较好用的我没办法只能使用了hash。抱歉会就行了。
方一道比较有意思的hash题可惜了我并没有成就感因为这道题本质上并不是我思考出来的哎。
这道题比较有意思求一个字符串中出现的东西 搞一波事情吧对于最长的我们把整个m便利完了之后一定能够求出,此时对于某个点我们应该二分一个长度使其达到答案这个答案相当的不好判断啊。
但是转换一个问题 对于一个i这个单词我们主席树中开这个东西然后查询就行了 啊二分+可持久化一下即可。妙!一个字符串的题目被转换成了数据结构的题目我也是佩服。
考虑一种更优的方法是尺取法 对于 l r r向右移动当移动到某个点的时候此时单词都有了 l向右移动此时不断的更新答案然后r再向右我们O(n)的时间貌似是求出了这个东西采用hash的话我想是可以O(n)的操作难度较高我们采用一定不会冲突的map 此时我们直接开个数组表示某个字符串出现了几次即可。完事了。
//#include<bits/stdc++.h>
#include<iostream>
#include<queue>
#include<iomanip>
#include<cctype>
#include<cstdio>
#include<deque>
#include<utility>
#include<cmath>
#include<ctime>
#include<cstring>
#include<string>
#include<cstdlib>
#include<vector>
#include<algorithm>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)>(y)?(y):(x))
#define INF 1000000000
#define ll long long
#define R register
#define mod 1000000007
using namespace std;
inline void put(R int x)
{
x<?putchar('-'),x=-x:;
R int num=;char ch[];
while(x)ch[++num]=x%+'',x/=;
num==?putchar(''):;
while(num)putchar(ch[num--]);
putchar('\n');return;
}
const int MAXN=;
int n,m,num,ans,s,answer=MAXN;
int vis[MAXN],cnt[MAXN];
map<string,int>k;
string a,b[MAXN];
int main()
{
ios::sync_with_stdio(false);
cin>>n;
for(int i=;i<=n;++i)
{
cin>>a;
if(!k[a])k[a]=++s;
//cout<<a<<endl;
}
cin>>m;
for(int i=;i<=m;++i)
{
cin>>b[i];
vis[k[b[i]]]=;
//cout<<b[i]<<endl;
}
for(int i=;i<=s;++i)if(vis[i])++ans;
if(ans==){put(ans),put();return ;}
for(int i=,j=;j<=m;++j)
{
int g=k[b[j]];
if(g)
{
++cnt[g];
if(cnt[g]==)++num;
}
while(num==ans)
{
//cout<<j<<' '<<i<<endl;
answer=min(answer,j-i);
++i;g=k[b[i]];
if(g)
{
--cnt[g];
if(cnt[g]==)--num;
}
}
}
put(ans);
put(answer);
return ;
}
这种尺取法显然是正确的如果非要证明的话我会采用一种答案最优性原则证明答案一定是答案所以这是正确的。
Trie 字典树啦很简单的东西可持久化的东西我想想好像是一个有根的东西等会我复习一下嘞。
首先是字典树我们对于根出发建立每一个单词的路径然后查询的时候直接查询即可。时间。
注意这里时间是总插入Trie数的字符串的长度而空间则是MAX最长子串*根的数量*子串数量*子串的最大数集。
很朴素的一道题目询问每篇文章出现了多少个单词其实就是建一颗Trie树在上面跑即可 然后我们考虑建文章的Trie树那么复杂度就是mn*20当然可以过但是我想AC自动机的话这个复杂度应该是O(m*5000)左右吧要快一点当然计算一下空间吧建文章的Trie 空间:n*5000*26=130000000*4=520000000b=520000kb=520MB刚好爆炸。。空间就给了128肿么办我的办法是直接开char 存。然后可以存下。
//#include<bits/stdc++.h>
#include<iostream>
#include<queue>
#include<iomanip>
#include<cctype>
#include<cstdio>
#include<deque>
#include<utility>
#include<cmath>
#include<ctime>
#include<cstring>
#include<string>
#include<cstdlib>
#include<vector>
#include<algorithm>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)>(y)?(y):(x))
#define INF 1000000000
#define ll long long
#define R register
#define l(x) c[x][0]
#define r(x) c[x][1]
using namespace std;
char buf[<<],*fs,*ft;
inline char getc()
{
return (fs==ft&&(ft=(fs=buf)+fread(buf,,<<,stdin),fs==ft))?:*fs++;
}
inline int read()
{
R int x=,f=;char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getchar();}
return x*f;
}
inline void put(R int x)
{
x<?putchar('-'),x=-x:;
R int num=;char ch[];
while(x)ch[++num]=x%+'',x/=;
num==?putchar(''):;
while(num)putchar(ch[num--]);
putchar('\n');return;
}
const int MAXN=,maxn=;
int n,m,cnt,flag=;
int trie[MAXN][],p;
int c[];
bool vis[MAXN];
char a[][MAXN][];
char b[maxn][];
bitset<>used[maxn];
inline void insert(int i,int j)
{
int len=strlen(a[i][j]);p=;
for(int k=;k<len;++k)
{
int now=a[i][j][k]-'a';
if(!trie[p][now])trie[p][now]=++cnt;
p=trie[p][now];
}
vis[p]=;
}
inline void calculate(int j)
{
int len=strlen(b[j]);p=;
for(int k=;k<len;++k)
{
if(b[j][k]<'a'||b[j][k]>'z')continue;
int now=b[j][k]-'a';
if(!trie[p][now])return;
p=trie[p][now];
}
if(vis[p])flag=;
}
int main()
{
n=read();
for(int i=;i<=n;++i)
{
c[i]=read();
for(int j=;j<=c[i];++j)scanf("%s",a[i][j]);
}
m=read();
for(int i=;i<=m;++i)scanf("%s",b[i]);
for(int i=;i<=n;++i)
{
memset(trie,,sizeof(trie));
memset(vis,,sizeof(vis));
cnt=;
for(int j=;j<=c[i];++j)insert(i,j);
for(int j=;j<=m;++j)
{
flag=;
calculate(j);
if(flag)used[j][i]=;
}
}
for(int j=;j<=m;++j)
{
flag=;
for(int i=;i<=n;++i)
if(used[j][i])
{
if(flag==){flag=;printf("%d",i);}
else printf(" %d",i);
}
putchar('\n');
}
return ;
}
一直wa 的原因是vis数组竟然没开 不是开了我又删了。。
一道简单的Trie树+dp 暴力dp 出奇迹A了。
//#include<bits/stdc++.h>
#include<iostream>
#include<queue>
#include<iomanip>
#include<cctype>
#include<cstdio>
#include<deque>
#include<utility>
#include<cmath>
#include<ctime>
#include<cstring>
#include<string>
#include<cstdlib>
#include<vector>
#include<algorithm>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)>(y)?(y):(x))
#define INF 1000000000
#define ll long long
#define R register
#define l(x) c[x][0]
#define r(x) c[x][1]
using namespace std;
char buf[<<],*fs,*ft;
inline char getc()
{
return (fs==ft&&(ft=(fs=buf)+fread(buf,,<<,stdin),fs==ft))?:*fs++;
}
inline int read()
{
R int x=,f=;char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getchar();}
return x*f;
}
inline void put(R int x)
{
x<?putchar('-'),x=-x:;
R int num=;char ch[];
while(x)ch[++num]=x%+'',x/=;
num==?putchar(''):;
while(num)putchar(ch[num--]);
putchar('\n');return;
}
const int MAXN=;
int n,p,cnt,m,maxx;
char b[],a[MAXN];
int trie[][];
int vis[];
int f[MAXN];
inline void insert()
{
int len=strlen(b+);p=;
for(int i=;i<=len;++i)
{
int now=b[i]-'a';
if(!trie[p][now])trie[p][now]=++cnt;
p=trie[p][now];
}
vis[p]=;
}
inline int query(int l,int r)
{
p=;
for(int i=l;i<=r;++i)
{
int now=a[i]-'a';
if(!trie[p][now])return ;
p=trie[p][now];
}
if(vis[p])return ;
return ;
}
int main()
{
n=read();m=read();
for(int i=;i<=n;++i)scanf("%s",b+),insert();
for(int i=;i<=m;++i)
{
scanf("%s",a+);
memset(f,,sizeof(f));
maxx=;f[]=;
int len=strlen(a+);
for(int j=;j<=len;++j)
{
int flag=;
for(int k=j-<?:j-;k<j;++k)
{
if(!f[k])continue;
flag=;
f[j]|=f[k]&query(k+,j);
if(f[j])break;
//cout<<k<<' '<<f[k]<<endl;
//cout<<query(k+1,j)<<endl;
}
if(f[j])maxx=max(maxx,j);
if(!flag)break;
}
put(maxx);
}
return ;
}
AC自动机 为什么要叫自动很简单因为它可以自动的匹配模式串我们继续我们的话题上午有点跑偏了。。。
自动之意取自会自己进行匹配而不需要人工的让其专门的操作原因在于一个fail数组 自闭了 我不写了。
KMP,HASH,Trie,AC自动机的更多相关文章
- KMP,Trie,AC自动机题目集
字符串算法并不多,KMP,trie,AC自动机就是其中几个最经典的.字符串的题目灵活多变也有许多套路,需要多做题才能体会.这里收集了许多前辈的题目做个集合,方便自己回忆. KMP题目:https:// ...
- AC自动机及KMP练习
好久都没敲过KMP和AC自动机了.以前只会敲个kuangbin牌板子套题.现在重新写了自己的板子加深了印象.并且刷了一些题来增加自己的理解. KMP网上教程很多,但我的建议还是先看AC自动机(Trie ...
- AC自动机讲解+[HDU2222]:Keywords Search(AC自动机)
首先,有这样一道题: 给你一个单词W和一个文章T,问W在T中出现了几次(原题见POJ3461). OK,so easy~ HASH or KMP 轻松解决. 那么还有一道例题: 给定n个长度不超过50 ...
- AC自动机详解 (P3808 模板)
AC自动机笔记 0.0 前言 哇,好久之前就看了 KMP 和 Trie 树,但是似乎一直没看懂 AC自动机?? 今天灵光一闪,加上之前看到一些博客和视频,瞬间秒懂啊... 其实这个玩意还是蛮好理解的. ...
- AC自动机简明教程
不会kmp和Trie树的请点击右上角X. AC自动机与kmp的唯一区别便是从单模式串变成了多模式串. 那么与kmp相同,AC自动机中的fail指针是指向当前状态的最长后缀. 当然这个后缀要在Trie树 ...
- AC自动机讲解超详细
begin:2019/5/2 感谢大家支持! AC自动机详细讲解 AC自动机真是个好东西!之前学KMP被Next指针搞晕了,所以咕了许久都不敢开AC自动机,近期学完之后,发现AC自动机并不是很难,特别 ...
- AC自动机
AC自动机,全称Aho-Corasick自动机.如果没记错的话好像就是前缀自动机. 其实AC自动机就是KMP上树的产物.理解了KMP,那AC自动机应该也是很好理解的. 与KMP类似,AC自动机也是扔一 ...
- 【BZOJ】1030: [JSOI2007]文本生成器(递推+ac自动机)
http://www.lydsy.com/JudgeOnline/problem.php?id=1030 其实做了1009也不会感到很难了,无非将kmp变成了ac自动机. 设f[i,j]表示前i个串当 ...
- bzoj3940&&bzoj3942 Ac自动机||kpm算法
方法就是维护一个动态栈 记录栈的每一位匹配到串的哪一位的编号 第一道kmp第二道ac自动机 自己理会 #include<cstdio> #include<cstring> #i ...
随机推荐
- BZOJ1294 洛谷P2566 状态压缩DP 围豆豆
传送门 题目描述 是不是平时在手机里玩吃豆豆游戏玩腻了呢?最近MOKIA手机上推出了一种新的围豆豆游戏,大家一起来试一试吧游戏的规则非常简单,在一个N×M的矩阵方格内分布着D颗豆子,每颗豆有不同的分值 ...
- .net core 服务注册生命周期
在Asp.Net core中的IServiceCollection容器中注册服务的生命周期分以下3种: 1.Transient 通过AddTransient注册,会在IServiceCollectio ...
- C#使用NPOI对Word文档进行导出操作的dll最新版2.5.1
Npoi导出非模板 最近使用NPOI做了个导出Word文档的功能,因为之前都是导出Excel很方便(不用模板),所以导出Word也选用了Npoi(也没有用模板,
- element-ui(vue版)使用switch时change回调函数中的形参传值问题
需求说明 有多个switch组件 需要知道switch的状态 表格中当前行(scope.row)的数据 问题描述 官方文档中对switch中change的描述: 目前能得到switch的状态值,但是无 ...
- Spring源码解析——核心类介绍
前言: Spring用了这么久,虽然Spring的两大核心:IOC和AOP一直在用,但是始终没有搞懂Spring内部是怎么去实现的,于是决定撸一把Spring源码,前前后后也看了有两边,很多东西看了就 ...
- requests接口自动化6-Body里json格式数据形式的post请求:json
Body里json格式数据形式的post请求:用json传参 fiddler里请求响应内容: 传递的json数据 [{"stepId":"0","ca ...
- 数据可视化之PowerQuery篇(二)这个方法帮你快速计算列
https://zhuanlan.zhihu.com/p/81846862 PowerQuery中,对两列或者多列的计算一般通过添加自定义列来实现,以下表为例, 如果需要1月和2月数据的合计,可以添加 ...
- 【Python】关于如何判断一个list是否为空的思考
前言 今天随手翻 stackoverflow,看到问题叫 How do I check if a list is empty? 一看这个问题,不难猜到到这是一个刚学 Python 的人提问的,因为这个 ...
- .NET Core微服务开发网关篇-ocelot
通过上篇我们知道,网关是外部访问的统一入口,本文采用Ocelot作为Api网关. 环境要求: vs2019 .NetCore3.1 Ocelot16.0.1 创建一个产品服务Api站点(AAStore ...
- Java设计模式 --- 七大常用设计模式示例归纳
设计模式分为三种类型,共23种: 创建型模式:单例模式.抽象工厂模式.建造者模式.工厂模式.原型模式 结构型模式:适配器模式.桥接模式.装饰模式.组合模式.外观模式.享元模式.代理模式 行为型模式:模 ...