KMP
\(s\) (长串)叫文本串,\(t\)(短串)叫模式串。
\(next_i\):在 \(p\) 中以 \(p[i]\) 结尾的后缀能够匹配的从 \(0\) 开始非平凡前缀的最大长度。
为什么这样做?考虑尝试的时候有顺序地做,只有这个顺序有道理。
P3375
这里字符串以 \(0\) 开始。注意 \(next_i\) 实际上是字符串中前缀的长度,也即前缀的结尾坐标往右一位。
并且你尝试匹配的时候,是从 \(next_{i - 1}\),也就是已经相等的字符串的后一位,看看是否与新加入的字符相等。之后也都是找到的是后一位。
写 KMP 的时候,脑子不能胡。
#include<bits/stdc++.h>
using namespace std;
#define f(i, a, b) for(int i = (a); i <= (b); i++)
#define cl(i, n) i.clear(),i.resize(n);
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int inf = 1e9;
string s, t;
int nxt[1000010];
int main() {
ios::sync_with_stdio(0);
cin.tie(NULL);
cout.tie(NULL);
cin >> s >> t;
int n = s.size(), m = t.size();
for(int i = 1, j = 0; i < m; i++) {
while(j >= 1 && t[i] != t[j])j = nxt[j - 1];
if(t[i] == t[j]) j++;
cout << i << " " << j << endl;
nxt[i] = j;
}
for(int i = 0, j = 0; i < n; i++) {
while(j >= 1 && t[i] != t[j])j = nxt[j - 1];
if(t[i] == t[j]) j++;
if(j == m) {
cout << i - m + 2 << endl;
j = nxt[j - 1];
}
}
f(i, 0, m - 1) cout << nxt[i] << " \n"[i == m];
return 0;
}
CF808G
【题意】给定字符串 \(s,t\),\(s\) 包含小写字母和问号,\(t\) 包含小写字母。求将所有 \(s\) 中问号改成任意字母的方案中,\(t\) 在 \(s\) 中出现最多次的方案。
【分析】考虑 DP。令 \(dp_{i,j}\) 表示 \(s_i\) 匹配 \(t_j\) 的最大匹配次数(是老套路了)。如遇到字母怎么办?
考虑枚举字母变成了什么。对于一个确定的字母,我们转移到一个最大的 \(k\),使得 \(s_{i + 1} = t_k\)。这为什么是对的呢?考虑反例类似这样:\(\mathtt{ababa?abacd; ababacd}\)。这时候问号应该匹配 \(t\) 的第二个字符。因为如果匹配上的是第四个字符,那么之后无法匹配 \(b\)。但是我们在之后发现,\(b\) 无法匹配的时候,会向下找能匹配的,也就是和之前匹配第二个字符是一样的效果。容易发现,不论是哪一个方案,如果它后面的方案都不能用的话,它一定能用上。因此每次找最大转移这个方案向下兼容,覆盖了所有方案。
那么向下兼容的应用场景是什么?考虑不同的字符之间能不能这样贪心。依然考虑上述的例子。如果问号处填 \(c\),那么是比 \(b\) 局部更优的方案。之后失配的时候,我们尝试换成 \(b\),是把 \(ababac+a\) 跳转到 \(abab+a\)。但是这时候我们发现,由于 \(ababa\) 不等于 \(abaca\)(因为问号处的 \(c\) 应该是 \(b\))切换不过来,因此没办法兼容。
预处理 \(jump_{i, c}\) 表示 \(t_{1,...,i} + c\)(\(c\) 是一个字符)也就是失配指针。
时间复杂度 \(O(26nm)\),但是我不太会写预处理,于是加上了 \(O(m^2)\)(其实是应该做到 \(O(26m)\) 的)但是特判以下问题不大。
mine O(26nm+m^2)
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define f(i, a, b) for(int i = (a); i <= (b); i++)
#define cl(i, n) i.clear(),i.resize(n);
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int inf = 1e9;
void cmax(int &x, int y) {if(x < y) x = y;}
void cmin(int &x, int y) {if(x > y) x = y;}
int nxt[100010];
int jump[100010][26];
vector<int> bd[100100];
vector<vector<int>> dp;
signed main() {
ios::sync_with_stdio(0);
cin.tie(NULL);
cout.tie(NULL);
//time_t start = clock();
//think twice,code once.
//think once,debug forever.
string s, t; cin >> s >> t;
int n = s.size(), m = t.size();if(n < m) {cout << 0 << endl; return 0;}
dp.resize(n + 2);
memset(jump, -1, sizeof(jump));
f(i, 0, n + 1) dp[i].resize(m + 2);
for(int i = 1, j = 0; i < m; i ++) {
while(t[i] != t[j] && j > 0) j = nxt[j - 1];
if(t[i] == t[j]) {
nxt[i] = j + 1;
j ++;
}
}
// f(i, 0, m) cout << nxt[i];
// cout << endl;
for(int i = 1; i <= m; i ++) {
int j = nxt[i - 1];
while(j > 0) {
int k = t[j];
if(jump[i][k - 'a'] < j) {
jump[i][k - 'a'] = j;
bd[i].push_back(j);
}
j = nxt[j - 1];
}
int k = t[j];
if(jump[i][k - 'a'] < j) {
jump[i][k - 'a'] = j;
bd[i].push_back(j);
}
}
// if(s.substr(0, 4) == "eeio") {return 0;}
// cout << n << endl;
// f(i, 0, m - 1) cout << nxt[i] << " \n"[i == m - 1];
// f(i, 0, m) f(k, 0, 25) cout << jump[i][k] << " \n"[k == 25];
// memset(dp, 0x3f, sizeof(dp));
// fill(dp.begin(), dp.end(), 0x3f3f3f3f);
//
s += "?";
f(i, 0, n + 1) f(j, 0, m + 1) dp[i][j] = -inf;
int ans = 0;
dp[0][0] = 0;
for(int i = -1; i <= n; i ++) {
for(int j = -1; j < m; j ++) {
// cout << i << " " << j << " " << dp[i + 1][j + 1] << endl;
cmax(ans, dp[1 + i][1 + j]);if(i == n) continue;
if(s[i + 1] == '?') {
// for(char k = 'a'; k <= 'z'; k ++) {
// if(s[i + 1] != '?' && s[i + 1] != k) continue;
if(j < m - 1) cmax(dp[1 + i + 1][1 + j + 1], dp[1 + i][1 + j]);
for(int k : bd[j + 1]){
// if(j < m - 1 && k == t[j + 1])
//else
cmax(dp[1 + i + 1][1 + k], dp[1 + i][1 + j] + (j == m - 1));
}
}
else {
char k = s[i + 1];
if(j < m - 1 && k == t[j + 1]) cmax(dp[1 + i + 1][1 + j + 1], dp[1 + i][1 + j]);
else cmax(dp[1 + i + 1][1 + jump[j + 1][k - 'a']], dp[1 + i][1 + j] + (j == m - 1));
}
}
}
cout << ans << endl;
//time_t finish = clock();
//cout << "time used:" << (finish-start) * 1.0 / CLOCKS_PER_SEC <<"s"<< endl;
return 0;
}
julao O(26nm + 26m)
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define f(i, a, b) for(int i = (a); i <= (b); i++)
#define cl(i, n) i.clear(),i.resize(n);
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int inf = 1e9;
void cmax(int &x, int y) {if(x < y) x = y;}
void cmin(int &x, int y) {if(x > y) x = y;}
int nxt[100010];
int jump[100010][26];
vector<int> bd[100100];
vector<vector<int> > dp;
signed main()
{
// freopen("1.in","r",stdin);
ios::sync_with_stdio(0);
cin.tie(NULL);
cout.tie(NULL);
string s, t; cin >> s >> t;
int n = s.size(), m = t.size();
// if(n < m) {cout << 0 << endl; return 0;}
dp.resize(n + 2);
memset(jump, -1, sizeof(jump));
f(i, 0, n + 1) dp[i].resize(m + 2);
for(int i = 1, j = 0; i < m; i ++) {
while(t[i] != t[j] && j > 0) j = nxt[j - 1];
if(t[i] == t[j])j ++;
nxt[i] = j;
}
t+='?';
for(int i=0;i<m+1;i++)
for(int j=0;j<26;j++)
if(i&&t[i]-'a'!=j) jump[i][j]=jump[nxt[i-1]][j];
else
{
if (t[i]-'a'==j) jump[i][j]=i+1;
else jump[i][j]=i;
}
f(i, 0, n ) f(j, 0, m) dp[i][j] = -inf;
int ans = 0;
dp[0][0] = 0;
for(int i=0;i<n;i++)
for(int j=0;j<m+1;j++)
if(s[i]!='?') cmax(dp[i+1][jump[j][s[i] - 'a']],dp[i][j]+(jump[j][s[i] - 'a'] == m));
else
{
for(int k=0;k<26;k++) cmax(dp[i+1][jump[j][k]],dp[i][j]+(jump[j][k]==m));
}
// for(int i=0;i<n;i++,puts("")) for(int j=0;j<m;j++) printf("%d ",dp[i][j]);
for(int i=0;i<m+1;i++) cmax(ans,dp[n][i]);
cout << ans << endl;
return 0;
}
/*
winlose???winl???w??
win
*/