字符串是一个非常庞大的家族,也是一个非常好用的数据结构,在字符串的问题中,最常见的莫过是模式匹配问题,也就是:
有一个模式串和主串,你需要判断这个模式串是否是主串的一部分
化串为数
在模式匹配问题中,有很多很多的前贤先哲都设计过很多算法,如著名的KMP算法,以及衍生而来的AC自动机,当然这些在我们的专题中后面都会讲到,今天,我们先将一个很巧妙地算法的基本思路,下一篇将具体实现过程,他的基本思路也很简单,也就是利用了计算机的基本原理,化串为数,也就是在Karp-rabin算法里,凡物皆数,宇宙皆数,都是把他转换成数来完成的,然后再把模式数串与主数串进行比较,就简单的多了。
实现转换
首先,我们要搞清楚怎样把串与数进行转换,他必定有一个基本规则,而且,我们明确的知道每一个字符有且只有对应一个数,不然的话,我们就会引起歧义。那么,有那么多的字符,我们怎么制定规则呢?
观察这个图片你可能已经发现了,这张图中第一排是含八个元素的向量,也就是,vector.我们需要先将字符串中的字符按照一定规则转化为数字,一般情况下我们使用ASCII码,随后我们可以通过找规律得出,每一个元素加1再乘以素数表中的第i的元素即可,就得到了他唯一对应的数字编码。
python版
def search(a, b):
hashA, hashB = 0, 0
aN = [ord(c)-ord('A')+1 for c in a]
bN = [ord(c)-ord('A')+1 for c in b]
m, n = len(aN), len(bN)
for i in range(m):
hashA = 26*hashA + aN[i]
hashB = 26*hashB + bN[i]
for j in range(m-1, n):
if j > m-1:
hashB -= bN[j-m]*(26**(m-1))
hashB = hashB*26 + bN[j]
if hashB == hashA:
if a == b[j-m+1:j+1]:
return True
return False
search( "AABA", "ACAADAABAAAAABABAA")
c++版
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int char_to_int(char c)
return c - '0';
int module(int x, int q) {
if (x < 0) {
return x % q + q;
}
else {
return x % q;
}
}
bool is_forge(char* P1, char* P2, int m) {
for (int i = 0; i < m; ++i) {
if (P1[i] != P2[i]) {
return true;
}
}
return false;
}
void rabin_karp(char* T, char* P, int d, int q, int m, int n) {
int x = pow(d, m - 1);
int h = module(x, q);
int p{};
int* t = new int[n - m] {};
for (int i = 0; i < m; ++i) {
p = module(d * p + char_to_int(P[i]), q);
t[0] = module(d * t[0] + char_to_int(T[i]), q);
}
for (int s = 0; s < n - m - 1; ++s) {
if (p == t[s]) {
if (!is_forge(P, T + s, m)) {
cout << "Pattern occurs with shift " << s - 1 << endl;
}
}
else {
t[s + 1] = module(d * (t[s] - char_to_int(T[s]) * h) + char_to_int(T[s + m]), q);
}
}
delete[]t;
}
int main(int argc, char* argv[]) {
vector<char> v{};
int d{ 10 };
cout << "please enter the radix : " << endl;
cin >> d;
int q{};
cout << "please enter the mod number (prime better) : " << endl;
cin >> q;
char element{};
cout << "please enter the pattern (end with 'a') :" << endl;
while (cin >> element) {
if (element != 'a') {
v.push_back(element);
}
else {
break;
}
}
int m = v.size();
char* P = new char[m] {};
int index{};
for_each(v.begin(), v.end(), [=](char x)mutable{ P[index++] = x; });
v.clear();
cout << "please enter the text (end with 'a') :" << endl;
while (cin >> element) {
if (element != 'a') {
v.push_back(element);
}
else {
break;
}
}
int n = v.size();
char* T = new char[n] {};
index = 0;
for_each(v.begin(), v.end(), [=](char x)mutable{ T[index++] = x; });
rabin_karp(T, P, d, q, m, n);
delete[]T;
delete[]P;
}