字符串——广义&二维KMP
广义KMP
普通KMP是关于字符序列的一种模式匹配算法,而广义KMP将字符序列推广到一般序列,即带有等价关系的序列均可以进行广义KMP算法。
即我们只需要定义序列中的元素是否相等或不相等,即可像普通KMP一样执行广义KMP算法。
二维KMP
广义KMP的一种特殊实现就是二维KMP,在二维KMP中的序列元素就是字符串。例如序列{“abcd”,“addfd”,“dwffas”},我们可以定义字符串相等,那么就可以在字符串序列上执行广义KMP算法,即二维KMP算法。
此题要求求一个二维字符序列中的二维最小循环子矩阵的面积。
我们可以将每一列当成一个字符串,则构成一个字符串序列。每一行当成一个字符串,构成一个字符串序列。对这两个字符串序列分别做KMP算法。然后分别求出最小循环节的长度,相乘便是答案,正确性显然。
vector<string> vec;
int kmp2(vector<string> &arr)
{
vector<int> nxt;
nxt.push_back(0);
for (int i = 1; i < arr.size(); i++)
{
int d = nxt[i - 1];
while (arr[d] != arr[i] && d != 0)
d = nxt[d - 1];
nxt.push_back(arr[d] == arr[i] ? d + 1 : 0);
}
return arr.size() - nxt.back();
}
void solve()
{
int n, m;
cin >> n >> m;
for (int i = 0; i < n; i++)
{
string str;
cin >> str;
vec.push_back(str);
}
vector<string> col, row;
for (int c = 0; c < m; c++)
{
string str;
for (int r = 0; r < n; r++)
str.push_back(vec[r][c]);
col.push_back(str);
}
for (int r = 0; r < n; r++)
{
string str;
for (int c = 0; c < m; c++)
str.push_back(vec[r][c]);
row.push_back(str);
}
cout << kmp2(col) * kmp2(row) << endl;
}
这题是上一题的加强版,我们只需要使用一层字符串hash来优化一维判断即可。
const int MOD353 = 998244353;
const int BASE = 114513;
char str[2005][2005];
ll bit[10005];
ll colh[2005][2005], rowh[2005][2005];
int nxt[2005];
struct Sequence
{
int p;
int l;
int r;
};
ll kmp2(int p1, int p2, int l, int r, ll vhash[][2005])
{
auto cmp = [&](int a, int b)
{
int len = r - l + 1;
ll ah = (vhash[a][r] - (vhash[a][l - 1] * bit[len]) % MOD353 + MOD353) % MOD353;
ll bh = (vhash[b][r] - (vhash[b][l - 1] * bit[len]) % MOD353 + MOD353) % MOD353;
return ah == bh;
};
nxt[0] = 0;
int n = (p2 - p1 + 1);
for (int i = 1; i < n; i++)
{
int d = nxt[i - 1];
while (!cmp(d + p1, i + p1) && d != 0)
d = nxt[d - 1];
nxt[i] = cmp(d + p1, i + p1) ? d + 1 : 0;
}
return n - nxt[n - 1];
}
void solve()
{
bit[0] = 1;
for (int i = 1; i < 10005; i++)
{
bit[i] = (bit[i - 1] * BASE) % MOD353;
}
int n, q;
scanf("%d %d", &n, &q);
for (int i = 1; i <= n; i++)
scanf("%s", str[i] + 1);
for (int c = 1; c <= n; c++)
{
ll *h = colh[c];
for (int r = 1; r <= n; r++)
h[r] = ((h[r - 1] * BASE) % MOD353 + str[r][c]) % MOD353;
}
for (int r = 1; r <= n; r++)
{
ll *h = rowh[r];
for (int c = 1; c <= n; c++)
h[c] = ((h[c - 1] * BASE) % MOD353 + str[r][c]) % MOD353;
}
for (int i = 0; i < q; i++)
{
int r1, c1, r2, c2;
cin >> r1 >> c1 >> r2 >> c2;
cout << kmp2(r1, r2, c1, c2, rowh) * kmp2(c1, c2, r1, r2, colh) << endl;
}
}