字符串匹配算法
字符串匹配算法
字符串匹配就是在主串
比较容易实现的字符串匹配算法:
- 暴力
- 字符串哈希
- KMP算法
算法一: 暴力
思路很简单:
让子串从第一个字符开始,与主串的每一个字符匹配,如果当前字符匹配上,则继续匹配下一个字符,如果匹配到子串的最后一个字符都相同,那就说明子串在主串中出现了。
例如:
主串 | a | b | c | a | b | c |
---|---|---|---|---|---|---|
子串 | b | c | a |
主串 | a | b | c | a | b | c |
---|---|---|---|---|---|---|
子串 | b | c | a |
主串 | a | b | c | a | b | c |
---|---|---|---|---|---|---|
子串 | b | c | a |
主串 | a | b | c | a | b | c |
---|---|---|---|---|---|---|
子串 | b | c | a |
算法二:字符串哈希
整体思路:
将一个字符串通过哈希函数变成数字,比较时只需比较数字是否相同即可。
获取哈希值的基本思路:
例如一个字符串
再利用前缀和,就可以处理出每个子串的哈希值。
如:
这里我们会想,要是有两个不同的字符串,对应同一个哈希值,那不就出问题了吗?
确实是这样,这个也叫做哈希冲突。所以我们为了避免哈希冲突,可以通过这种手段:
让
在代码上,我们可以用
由于
例题:
给定一个长度为
字符串中只包含大小写英文字母和数字。
输入格式
第一行包含整数
第二行包含一个长度为
接下来
注意,字符串的位置从
输出格式
对于每个询问输出一个结果,如果两个字符串子串完全相同则输出 Yes
,否则输出 No
。
每个结果占一行。
数据范围
输入样例:
8 3
aabbaabb
1 3 5 7
1 3 6 8
1 2 1 2
输出样例:
Yes
No
Yes
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+10,P = 131;
unsigned long long p[N],h[N];
unsigned long long find(int l,int r)
{
return h[r] - h[l-1]*p[r-l+1];
}
int main()
{
int n,m;
string s;
cin >>n>>m;
cin >>s;
s = ' '+s;
p[0] = 1;
for (int i=1;i<=n;i++)
{
p[i] = p[i-1]*P;
h[i] = h[i-1]*P + s[i];
}
while (m--)
{
int l1,r1,l2,r2;
cin >>l1>>r1>>l2>>r2;
if (find(l1,r1) == find(l2,r2)) cout <<"Yes"<<endl;
else cout <<"No"<<endl;
}
}
原题链接:
算法三:
大名鼎鼎的
首先,我们知道了暴力匹配算法,但其实在暴力匹配的过程中,我们做了很多不必要的操作。比如在字符串
像这样:
主串 | A | B | A | B | A | B | C | A | A |
---|---|---|---|---|---|---|---|---|---|
子串 | A | B | A | B | C |
主串 | A | B | A | B | A | B | C | A | A |
---|---|---|---|---|---|---|---|---|---|
子串 | A | B | A | B | C |
我们为什么可以这样做呢?
因为在第一次匹配的时候,我们发现已经匹配上的
这里为我们提供了一个思路,如果可以知道每次匹配到不相同,我们的子串应该移动到哪个位置再继续匹配,那可以大大加快匹配的速度。这里我们发现,主串的指针其实不需要回退,只要子串的指针移动到正确的位置即可。
那我们的子串应该移动到哪个位置呢?
这里引入一个
先不说
字符串 | A | B | A | B | C |
---|---|---|---|---|---|
下标 | 1 | 2 | 3 | 4 | 5 |
0 | 0 | 1 | 2 | 0 |
对照这个表,再进行一次匹配:
主串 | A | B | A | B | A | B | C | A | A |
---|---|---|---|---|---|---|---|---|---|
指针 | |||||||||
子串 | A | B | A | B | C | ||||
指针 |
我们发现在指针
主串 | A | B | A | B | A | B | C | A | A |
---|---|---|---|---|---|---|---|---|---|
指针 | |||||||||
子串 | A | B | A | B | C | ||||
指针 |
注:我们这里每次比较的是主串的
匹配的代码可以这样实现:
//s是主串 p是子串
for (int i=1,j=0;i<=n;i++)
{
while (j && s[i] != p[j+1]) j = ne[j]; //如果不匹配,j一直后退到能匹配 或者一直退到起点 j=0
if (s[i] == p[j+1]) j++;
if (j == m) //这里是匹配成功了,如果要继续匹配就让 j = ne[j] 继续匹配
{
j = ne[j];
}
}
//ne[1] = 0
for (int i=2,j=0;i<=n;i++)
{
while (j && p[i] != p[j+1]) j = ne[j];
if (p[i] == p[j+1]) j++;
ne[i] = j; //记录下next数组
}
例题:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?