我们定义 字符串的模运算 : T m o d P T \bmod P TmodP 结果为 T T T 的最长后缀 , 且同时为 P P P 的最长前缀.
例如 P = aba P=\text{aba} P=aba
a m o d P = a \text{a} \bmod P = \text{a} amodP=a
b m o d P = ∅ \text{b} \bmod P = \empty bmodP=∅
ab m o d P = ab \text{ab} \bmod P = \text{ab} abmodP=ab
ba m o d P = ∅ \text{ba} \bmod P = \empty bamodP=∅
baa m o d P = a \text{baa} \bmod P = \text{a} baamodP=a
aba m o d P = aba \text{aba} \bmod P = \text{aba} abamodP=aba
⋯ \cdots ⋯
若 T m o d P T \bmod P TmodP 等于 P P P 本身 , 则 P P P 在 T T T 末尾处出现一次.
根据定义, 我们可以得出以下性质:
记 a a a 是一个字符 , S S S 是一个字符串 , P P P 给定.
a a a 拼在 S S S 前面 :
a S m o d P = { a S S m o d P aS \bmod P= \begin{cases} aS\\ S \bmod P \end{cases} aSmodP={aSSmodP
a a a 拼在 S S S 后面 :
S a m o d P = ( ( S m o d P ) + a ) m o d P Sa \bmod P= ((S \bmod P) + a ) \bmod P SamodP=((SmodP)+a)modP
( + + + 也表示拼接)
考虑到对 P P P 取模后结果是 P P P 的前缀 , 我们也可以用数字表示对 P P P 取模后的前缀长度. 如 k k k 就表示 P [ 0... k ) P[0...k) P[0...k).
现我们已知一个字符串 T T T , T m o d P = k T \bmod P =k TmodP=k 和一个字符 c c c, 我们想知道 T c m o d P Tc \bmod P TcmodP 的结果.
T c m o d P = ( ( T m o d P ) + c ) m o d P Tc \bmod P = ((T \bmod P) + c) \bmod P TcmodP=((TmodP)+c)modP (性质 2)
= ( P [ 0... k ) + c ) m o d P =(P[0...k)+c) \bmod P =(P[0...k)+c)modP
定义函数 go(k,c) \text{go(k,c)} go(k,c) 来求解 ( P [ 0... k ) + c ) m o d P (P[0...k)+c) \bmod P (P[0...k)+c)modP.
定义 f ( k ) f(k) f(k) 表示 P [ 1... k ) m o d P P[1...k) \bmod P P[1...k)modP 的结果. 即 P [ 1... k ) m o d P P[1...k) \bmod P P[1...k)modP 等于 P [ 0... f ( k ) ) P[0...f(k)) P[0...f(k)). 容易发现 , 因为 P [ 1... k ) P[1...k) P[1...k) 只有 k − 1 k-1 k−1 位 , 所以 f ( k ) f(k) f(k) 严格小于 k k k.
对于 go(k,c) \text{go(k,c)} go(k,c) , 分几种情况:
P [ 0... k ) + c = k ( P [ k ] = c ) P[0...k)+c=k \qquad (P[k]=c) P[0...k)+c=k(P[k]=c)
答案为 P [ 0... k ] P[0...k] P[0...k] , 即 P [ 0... k + 1 ) P[0...k+1) P[0...k+1) , 即前缀长度为 k + 1 k+1 k+1 .
P [ 0... k ) + c ≠ k ( P [ k ] ≠ c ) P[0...k)+c \neq k \qquad (P[k]\neq c) P[0...k)+c=k(P[k]=c)
P [ 0 ] P[0] P[0] 对于运算是多余的 (结合上述图片理解)
那么答案为 ( P [ 1... k ) + c ) m o d P (P[1...k)+c) \bmod P (P[1...k)+c)modP , 也就是 ( P [ 0... f ( k ) ) + c ) m o d P (P[0...f(k)) + c) \bmod P (P[0...f(k))+c)modP , 即 go(f(k),c) \text{go(f(k),c)} go(f(k),c). 而 f ( k ) < k f(k)
找出 p a t t e r n pattern pattern 在 t e x t text text 中出现的位置
题目
本题还要求出 p a t t e r n [ 0... i ) pattern[0...i) pattern[0...i) 的 b o r d e r border border 长度 ( b o r d e r border border 定义见题面).
而 P [ 0... i ) m o d P = f ( i ) = P [ 0... f ( i ) ) P[0...i) \bmod P = f(i) = P[0...f(i)) P[0...i)modP=f(i)=P[0...f(i)) , 根据字符串取模的定义 , f ( i ) f(i) f(i) 即为 P [ 0... i ) P[0...i) P[0...i) 的 b o r d e r border border 长度.
#include
using namespace std;
const int MAXN = 1e6 + 5;
string text, pattern;
int f[MAXN];
int go(int k, char c)
{
if(pattern[k] == c) return k + 1;
else if(k > 0) return go(f[k], c);
else return 0;
}
int main()
{
cin >> text >> pattern;
f[1] = 0; // P[1,1) mod P == 0
for(int i=1; i<pattern.size(); i++)
f[i + 1] = go(f[i], pattern[i]);
int res = 0;
for(int i=0; i<text.size(); i++)
{
res = go(res, text[i]);
if(res == pattern.size()) cout << i -pattern.size() + 2 << "\n";
}
for(int i=0; i<pattern.size(); i++)
cout << f[i + 1] << " ";
return 0;
}
题目
记 E ( S ) E(S) E(S) 表示当前字符串为 S S S , 停止时长度的期望.
若 P = 101 P=101 P=101
E ( ∅ ) = 1 2 E ( 0 ) + 1 2 E ( 1 ) + 1 E(\empty) = \frac 1 2 E(0) + \frac 1 2 E(1) +1 E(∅)=21E(0)+21E(1)+1
E ( 1 ) = 1 2 E ( 10 ) + 1 2 E ( 11 ) + 1 E(1) = \frac 1 2 E(10) + \frac 1 2 E(11) + 1 E(1)=21E(10)+21E(11)+1
E ( 10 ) = 1 2 E ( 100 ) + 1 2 E ( 101 ) + 1 E(10) = \frac 1 2 E(100) + \frac 1 2 E(101) + 1 E(10)=21E(100)+21E(101)+1
E ( 101 ) = 0 E(101) = 0 E(101)=0
…
这样显然应该有无限项 , 但如果字符串 A , B A,B A,B 满足 A ≡ B ( m o d P ) A \equiv B \pmod P A≡B(modP) , 有 E ( A ) = E ( B ) E(A)=E(B) E(A)=E(B) , 因此我们只关注字符串 S m o d P S\bmod P SmodP 后的结果.
记 E ( S ) E(S) E(S) 表示当前字符串 m o d P \bmod P modP 结果为 S S S , 停止时长度的期望.
那么便只有:
E ( ∅ ) = 1 2 E ( ∅ ) + 1 2 E ( 1 ) + 1 E(\empty) = \frac 1 2 E(\empty) + \frac 1 2 E(1) +1 E(∅)=21E(∅)+21E(1)+1
E ( 1 ) = 1 2 E ( 10 ) + 1 2 E ( 1 ) + 1 E(1) = \frac 1 2 E(10) + \frac 1 2 E(1) + 1 E(1)=21E(10)+21E(1)+1
E ( 10 ) = 1 2 E ( ∅ ) + 1 2 E ( 101 ) + 1 E(10) = \frac 1 2 E(\empty) + \frac 1 2 E(101) + 1 E(10)=21E(∅)+21E(101)+1
E ( 101 ) = 0 E(101) = 0 E(101)=0
用数字表示前缀,同时将式子左右同乘以 2 2 2 :
2 E ( 0 ) = E ( 0 ) + E ( 1 ) + 2 2E(0) = E(0) + E(1) + 2 2E(0)=E(0)+E(1)+2
2 E ( 1 ) = E ( 2 ) + E ( 1 ) + 2 2E(1) = E(2) + E(1) + 2 2E(1)=E(2)+E(1)+2
2 E ( 2 ) = E ( 0 ) + E ( 3 ) + 2 2E(2) = E(0) + E(3) + 2 2E(2)=E(0)+E(3)+2
E ( 3 ) = 0 E(3) = 0 E(3)=0
写成一般形式 : 2 E ( i ) = E ( i + 1 ) + E ( g i ) + 2 2E(i)=E(i+1) + E(g_i)+2 2E(i)=E(i+1)+E(gi)+2
其中 g i g_i gi 表示 ( P [ 0... i ) + c ) m o d P (P[0...i)+c )\bmod P (P[0...i)+c)modP , 其中 c c c 为 P i ‾ \overline{P_i} Pi ( P i P_i Pi 取反), 即 g i = g o ( i , P i ‾ ) g_i=go(i, \overline{P_i}) gi=go(i,Pi)
由第一个式子,我们可以将 E ( 1 ) E(1) E(1) 表示为 a 1 × E ( 0 ) + b 1 a_1\times E(0)+b_1 a1×E(0)+b1
以此类推 , 将 E ( i ) E(i) E(i) 表示为 a i × E ( 0 ) + b i a_i \times E(0) + b_i ai×E(0)+bi :
2 E ( i ) = E ( i + 1 ) + E ( g i ) + 2 2E(i)=E(i+1) + E(g_i)+2 2E(i)=E(i+1)+E(gi)+2
E ( i + 1 ) = 2 E ( i ) − E ( g i ) − 2 E(i + 1) = 2 E(i) - E(g_i) - 2 E(i+1)=2E(i)−E(gi)−2
a i + 1 × E ( 0 ) + b i + 1 = ( 2 a i − a g i ) ∗ E ( 0 ) + ( 2 b i − b g i − 2 ) a_{i+1} \times E(0) + b_{i+1} = (2a_i - a_{g_i}) * E(0) + (2b_i - b_{g_i} - 2) ai+1×E(0)+bi+1=(2ai−agi)∗E(0)+(2bi−bgi−2)
得 a i + 1 = 2 a i − a g i , b i + 1 = 2 b i − b g i − 2 a_{i+1}=2a_i - a_{g_i}, b_{i+1}=2b_i - b_{g_i} - 2 ai+1=2ai−agi,bi+1=2bi−bgi−2
由于 E ( 0 ) = E ( 0 ) E(0) = E(0) E(0)=E(0) , 即 a 0 = 1 a_0=1 a0=1 , 剩下的 a i + 1 = 2 a i − a g i a_{i+1}=2a_i - a_{g_i} ai+1=2ai−agi 类推均为 1 1 1 , 得 a a a 永远为 1 1 1.
最终带入 E ( n ) = a n × E ( 0 ) + b n = E ( 0 ) + b n = 0 E(n)=a_n \times E(0) + b_n=E(0)+b_n=0 E(n)=an×E(0)+bn=E(0)+bn=0 , 求解 E ( 0 ) = − b n E(0)=-b_n E(0)=−bn .
#include
#define int long long
using namespace std;
const int MAXN = 5e5 + 5;
const int mod = 1e9 + 7;
int n, p[MAXN], f[MAXN], g[MAXN], a[MAXN], b[MAXN];
int go(int k, int c)
{
if(p[k] == c) return k + 1;
else if(k > 0) return go(f[k], c);
else return 0;
}
signed main()
{
cin >> n;
for(int i=0; i<n; i++)
{
char c; cin >> c;
p[i] = c - '0';
}
f[1] = 0;
for(int i=1; i<n; i++)
f[i + 1] = go(f[i], p[i]);
for(int i=0; i<n; i++)
g[i] = go(i, p[i] ^ 1);
for(int i=0; i<n; i++)
b[i + 1] = ( 2 * b[i] - b[g[i]] - 2 ) % mod;
cout << ( -b[n] + mod ) % mod;
return 0;
}
题目
首先介绍暴力算法:before 表示已经处理完的,after 表示待处理的.
#include
using namespace std;
string pattern, text;
int dfs(string before, string after)
{
if(before.size() >= pattern.size())
if(before.substr(before.size() - pattern.size()) == pattern) return 1e9;
if(after == "") return 0;
int r1 = dfs(before + after[0], after.substr(1));
int r2 = dfs(before, after.substr(1)) + 1;
return min(r1, r2);
}
int main()
{
cin >> pattern >> text;
cout << dfs("", text);
return 0;
}
记 after’ 为 after 串开始的位置:
#include
using namespace std;
string pattern, text;
int dfs(string before, int after)
{
if(before.size() >= pattern.size())
if(before.substr(before.size() - pattern.size()) == pattern) return 1e9;
if(after == text.size()) return 0;
char c = text[after];
int r1 = dfs(before + c, after + 1);
int r2 = dfs(before, after + 1) + 1;
return min(r1, r2);
}
int main()
{
cin >> pattern >> text;
cout << dfs("", 0);
return 0;
}
记 before’ 为 before 串 m o d P \bmod P modP 的结果
#include
using namespace std;
const int MAXN = 1e4 + 5;
string pattern, text;
int f[MAXN];
int go(int k, char c)
{
if(pattern[k] == c) return k + 1;
else if(k > 0) return go(f[k], c);
else return 0;
}
int dfs(int before, int after)
{
if(before == pattern.size()) return 1e9;
if(after == text.size()) return 0;
char c = text[after];
int r1 = dfs(go(before, c), after + 1);
int r2 = dfs(before, after + 1) + 1;
return min(r1, r2);
}
int main()
{
cin >> pattern >> text;
for(int i=1; i<pattern.size(); i++)
f[i+1] = go(f[i], pattern[i]);
cout << dfs(0, 0);
return 0;
}
加上记忆化搜索:
#include
using namespace std;
const int MAXN = 1e4 + 5;
const int MAXM = 5005;
string pattern, text;
int f[MAXN];
int go(int k, char c)
{
if(pattern[k] == c) return k + 1;
else if(k > 0) return go(f[k], c);
else return 0;
}
bool mem[MAXM][MAXN];
int cache[MAXM][MAXN];
int dfs(int before, int after)
{
if(before == pattern.size()) return 1e9;
if(after == text.size()) return 0;
if(mem[before][after]) return cache[before][after];
mem[before][after] = true;
char c = text[after];
int r1 = dfs(go(before, c), after + 1);
int r2 = dfs(before, after + 1) + 1;
return cache[before][after] = min(r1, r2);
}
int main()
{
cin >> pattern >> text;
for(int i=1; i<pattern.size(); i++)
f[i+1] = go(f[i], pattern[i]);
cout << dfs(0, 0);
return 0;
}
此时 , 还有一个点超时 , 原因为 g o go go 函数并非 O ( 1 ) O(1) O(1) , 考虑预处理出所有 g o go go 函数的值.
#include
using namespace std;
const int MAXN = 1e4 + 5;
const int MAXM = 5005;
string pattern, text;
int f[MAXN], g[MAXM][26];
int go(int k, char c)
{
if(pattern[k] == c) return k + 1;
else if(k > 0) return go(f[k], c);
else return 0;
}
bool mem[MAXM][MAXN];
int cache[MAXM][MAXN];
int dfs(int before, int after)
{
if(before == pattern.size()) return 1e9;
if(after == text.size()) return 0;
if(mem[before][after]) return cache[before][after];
mem[before][after] = true;
int c = text[after] - 'a';
int r1 = dfs(g[before][c], after + 1);
int r2 = dfs(before, after + 1) + 1;
return cache[before][after] = min(r1, r2);
}
int main()
{
cin >> pattern >> text;
for(int i=1; i<pattern.size(); i++)
f[i+1] = go(f[i], pattern[i]);
for(int c=0; c<26; c++)
for(int i=0; i<pattern.size(); i++)
g[i][c] = go(i, c + 'a');
cout << dfs(0, 0);
return 0;
}