227 高翊恩
#include <bits/stdc++.h>
using namespace std;
int main() {
string s = "abcdefg";
cout << s.size() << endl; // 7
cout << s.find('f') << endl; // 5
cout << s.find('z') << endl; // 4294967295
cout << s.substr(2, 3) << endl; // "cde"
return 0;
}
#include <bits/stdc++.h>
using namespace std;
string Prefix(string s, string i) {
return s.substr(0, i);
}
string Suffix(string s, string i) {
return s.substr(i, s.size() - i);
}
int main() {
string s = "abcdefg";
cout << Prefix(s, 3) << endl; // "abc"
cout << Suffix(s, 4) << endl; // "defg"
cout << Prefix(s, 0) << endl; // ""
return 0;
}
#include <bits/stdc++.h>
using namespace std;
bool isPD(string s) {
int n = s.size();
for (int i = 0; i < (n >> 1); i++) if (s[i] != s[n - i + 1]) return false;
return true;
}
#include <bits/stdc++.h>
using namespace std;
int main() {
vector<string> v;
for (auto &i : v) cin >> i;
sort(v.begin(), v.end());
for (auto &i : v) cout << i << endl;
return 0;
}
aa
aba
bb
bba
試試看:
aa
aba
bb
bba
試試看:
struct TrieNode {
int C;
vector<TrieNode*> child;
TrieNode() {
C = 0;
child = vector<TrieNode*>(26, nullptr);
}
void push(string s) {
TrieNode *now = this;
for (auto &i : s) {
if (now -> child[i - 'a'] == nullptr) now -> child[i - 'a'] = new TrieNode();
now = now -> child[i - 'a'];
}
now -> C++;
}
};
其實滿單純的
O(n2logn)
來把log壓掉
來把log壓掉
dp[] | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
val | 1 | 0 | 1 | 0 |
a
a
b
b
c
dp[] | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
val | 1 | 0 | 1 | 0 |
a
a
b
b
c
dp[] | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
val | 1 | 0 | 1 | 0 |
a
a
b
b
c
dp[] | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
val | 1 | 0 | 1 | 0 |
a
a
b
b
c
dp[] | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
val | 1 | 0 | 1 | 0 |
a
a
b
b
c
dp[] | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
val | 1 | 0 | 1 | 0 |
a
a
b
b
c
dp[] | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
val | 1 | 0 | 1 | 0 |
a
a
b
b
c
dp[] | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
val | 1 | 0 | 1 | 0 | 2 |
a
a
b
b
c
16進位制
Σ={0…9,A…F}
轉換方式:
8E5C16=3644410
26進位制
Σ={a…z}
轉換方式:
pring26=715918410
問題1
a→0
pring26=aaaaapring26
27進位制
Σ={a…z}
{a,b,…,z}→{1,2,…,26}
轉換方式:
pring27=886429610
問題2
數字太大啦!!
所以送它一個%
27進位制
Σ={a…z}
{a,b,…,z}→{1,2,…,26}
最後要%(1e9+7)
轉換方式:
pring27=886429610
正著做:
Hash(s)=(s0p0+s1p1+s2p2+⋯+s∣s∣−1p∣s∣−1)%M
反著做:
hsaH(s)=(s0p∣s∣−1+s1p∣s∣−2+s2p∣s∣−3+⋯+s∣s∣−1p0)%M
Hash做字串匹配
s=abbabbab
t=abbab
Hash(t)=1084078
[from, to) | substring of s | Hash() |
---|---|---|
[0, 5) | abbab | 1084078 |
[1, 6) | bbabb | 1103033 |
[2, 7) | babba | 572294 |
[3, 8) | abbab | 1084078 |
可不可以利用Hash(s.substr(0,i)),只用O(1)求出Hash(s.substr(1,i))?
s0s1s2s3s4⟶h
s1s2s3s4⟶h−s0p∣t∣
s1s2s3s4_⟶(h−s0p∣t∣)×27
s1s2s3s4s5⟶(h−s0p∣t∣)×27+s5
記得有模運算
這裡用的是hsaH
開一個陣列Pref記錄Hash(Ps[i])
則可以用O(1)求出(i=l∑r−1sipi)%M
可是我們要的是(i=l∑r−1sipi−l)%M
int mod;
int PLUS(int x, int y) {
return (x + y) % mod;
}
int MINUS(int x, int y) {
return (x - (y % mod) + mod) % mod;
}
int TIMES(int x, int y) {
return (x * y) % mod;
}
Q×Q−1=1
⇒Q×Q−1≡1(modM)
所以想辦法找到Q−1就好了
費馬小定理
ap≡a(modp)
a<p⇒ap−1≡1(modp)
⇒ap−2≡a−1(modp)
int modPow(int a, int x) {
int ans = 1;
for (int i = 1 << 30; i > 0; i >>= 1) {
ans = ans * ans % mod;
if (i & x) ans = ans * a % mod;
}
return ans;
}
int DIVIDE(int x, int y) {
return x * modPow(y, mod - 2) % mod;
}
int mod;
int modPow(int a, int x) {
int ans = 1;
for (int i = 1 << 30; i > 0; i >>= 1) {
ans = ans * ans % mod;
if (i & x) ans = ans * a % mod;
}
return ans;
}
struct Hash {
int p;
vector<int> pref;
Hash(string s, int _p) {
p = _p;
int n = s.size();
pref.resize(n + 1);
int mul = 1;
pref[0] = 0;
for (int i = 0; i < n; i++) {
pref[i + 1] = (pref[i] + (s[i] - 'a' + 1) * mul) % mod;
mul = mul * p % mod;
}
}
int query(int from, int len) {
int ans = (pref[from + len] - pref[from] + mod) % mod;
return ans * modpow(modpow(p, mod - 2), from) % mod;
}
};
我喜歡叫它CPS (Common Prefix and Suffix)
字串 | CPS(0) | CPS(1) | CPS(2) | CPS(3) |
---|---|---|---|---|
"abcabca" | "abcabca" | "abca" | "a" | "" |
"abcde" | "abcde" | "" | --- | --- |
"zzz" | "zzz" | "zz" | "z" | "" |
字串 | CPS(0) | CPS(1) | CPS(2) | CPS(3) |
---|---|---|---|---|
"abcabca" | 7 | 4 | 1 | 0 |
"abcde" | 5 | 0 | -1 | -1 |
"zzz" | 3 | 2 | 1 | 0 |
s=abbabbab
s的前綴 | CPS(0) | CPS(1) | CPS(2) | CPS(3) |
---|---|---|---|---|
"" | "" | --- | --- | --- |
"a" | "a" | "" | --- | --- |
"ab" | "ab" | "" | --- | --- |
"abb" | "abb" | "" | --- | --- |
"abba" | "abba" | "a" | "" | --- |
"abbab" | "abbab" | "ab" | "" | --- |
"abbabb" | "abbabb" | "abb" | "" | --- |
"abbabba" | "abbabba" | "abba" | "a" | "" |
"abbabbab" | "abbabbab" | "abbab" | "ab" | "" |
s=abbabbab
Ps[i] | CPS(0) | CPS(1) | CPS(2) | CPS(3) |
---|---|---|---|---|
0 | 0 | -1 | -1 | -1 |
1 | 1 | 0 | -1 | -1 |
2 | 2 | 0 | -1 | -1 |
3 | 3 | 0 | -1 | -1 |
4 | 4 | 1 | 0 | -1 |
5 | 5 | 2 | 0 | -1 |
6 | 6 | 3 | 0 | -1 |
7 | 7 | 4 | 1 | 0 |
8 | 8 | 5 | 2 | 0 |
Ps[i] | CPS(0) | CPS(1) | CPS(2) | CPS(3) |
---|---|---|---|---|
0 | 0 | -1 | -1 | -1 |
1 | 1 | 0 | -1 | -1 |
2 | 2 | 0 | -1 | -1 |
3 | 3 | 0 | -1 | -1 |
4 | 4 | 1 | 0 | -1 |
5 | 5 | 2 | 0 | -1 |
6 | 6 | 3 | 0 | -1 |
7 | 7 | 4 | 1 | 0 |
8 | 8 | 5 | 2 | 0 |
↑ 這一行沒用
Ps[i] | CPS(1) | CPS(2) | CPS(3) |
---|---|---|---|
0 | -1 | -1 | -1 |
1 | 0 | -1 | -1 |
2 | 0 | -1 | -1 |
3 | 0 | -1 | -1 |
4 | 1 | 0 | -1 |
5 | 2 | 0 | -1 |
6 | 3 | 0 | -1 |
7 | 4 | 1 | 0 |
8 | 5 | 2 | 0 |
觀察性質
Ps[i] | CPS(1) | CPS(2) | CPS(3) |
---|---|---|---|
0 | -1 | -1 | -1 |
1 | 0 | -1 | -1 |
2 | 0 | -1 | -1 |
3 | 0 | -1 | -1 |
4 | 1 | 0 | -1 |
5 | 2 | 0 | -1 |
6 | 3 | 0 | -1 |
7 | 4 | 1 | 0 |
8 | 5 | 2 | 0 |
觀察性質
Ps[i] | CPS(1) | CPS(2) | CPS(3) |
---|---|---|---|
0 | -1 | -1 | -1 |
1 | 0 | -1 | -1 |
2 | 0 | -1 | -1 |
3 | 0 | -1 | -1 |
4 | 1 | 0 | -1 |
5 | 2 | 0 | -1 |
6 | 3 | 0 | -1 |
7 | 4 | 1 | 0 |
8 | 5 | 2 | 0 |
觀察性質
Ps[i] | CPS(1) | CPS(2) | CPS(3) |
---|---|---|---|
0 | -1 | -1 | -1 |
1 | 0 | -1 | -1 |
2 | 0 | -1 | -1 |
3 | 0 | -1 | -1 |
4 | 1 | 0 | -1 |
5 | 2 | 0 | -1 |
6 | 3 | 0 | -1 |
7 | 4 | 1 | 0 |
8 | 5 | 2 | 0 |
觀察性質
s.CPS(2)=s.CPS(1).CPS(1)
abbabba → abba → a
abba → a
abbabba
abbabba
abbabba
abba
abbabba
abba
abbabba
abba
abbabba
abba
a
abbabba
abba
a
我們可以推得:
若t∈s.CPS
則t.CPS⊂s.CPS
根據定義:s.CPS(1)為「除了s本身的最長CPS」
再認真想一下,我們可以發現
s.CPS(a+b)=s.CPS(a).CPS(b)
Ps[i] | CPS(1) | CPS(2) | CPS(3) |
---|---|---|---|
0 | -1 | -1 | -1 |
1 | 0 | -1 | -1 |
2 | 0 | -1 | -1 |
3 | 0 | -1 | -1 |
4 | 1 | 0 | -1 |
5 | 2 | 0 | -1 |
6 | 3 | 0 | -1 |
7 | 4 | 1 | 0 |
8 | 5 | 2 | 0 |
Ps[i] | CPS(1) | CPS(2) | CPS(3) |
---|---|---|---|
0 | -1 | -1 | -1 |
1 | 0 | -1 | -1 |
2 | 0 | -1 | -1 |
3 | 0 | -1 | -1 |
4 | 1 | 0 | -1 |
5 | 2 | 0 | -1 |
6 | 3 | 0 | -1 |
7 | 4 | 1 | 0 |
8 | 5 | 2 | 0 |
Ps[i] | CPS(1) | CPS(2) | CPS(3) |
---|---|---|---|
0 | -1 | -1 | -1 |
1 | 0 | -1 | -1 |
2 | 0 | -1 | -1 |
3 | 0 | -1 | -1 |
4 | 1 | 0 | -1 |
5 | 2 | 0 | -1 |
6 | 3 | 0 | -1 |
7 | 4 | 1 | 0 |
8 | 5 | 2 | 0 |
Ps[i] | CPS(1) | CPS(2) | CPS(3) |
---|---|---|---|
0 | -1 | -1 | -1 |
1 | 0 | -1 | -1 |
2 | 0 | -1 | -1 |
3 | 0 | -1 | -1 |
4 | 1 | 0 | -1 |
5 | 2 | 0 | -1 |
6 | 3 | 0 | -1 |
7 | 4 | 1 | 0 |
8 | 5 | 2 | 0 |
令t為Ps的其中一個元素
則有t.CPS⊆Ps
如果我們對所有s的前綴,記錄其CPS(1)
則t.CPS(2)=t.CPS(1).CPS(1)
很容易就可以得到t.CPS(2)
又t.CPS(3)=t.CPS(2).CPS(1)
很容易就可以得到t.CPS(3)
...
Ps[i] | CPS(1) |
---|---|
0 | -1 |
1 | 0 |
2 | 0 |
3 | 0 |
4 | 1 |
5 | 2 |
6 | 3 |
7 | 4 |
8 | 5 |
當我們要找s的第7個前綴的所有共同前後綴時
Ps[7].CPS(1)
Ps[7].CPS(2)
Ps[7].CPS(3)
s=abbabbab
πs={−1,0,0,0,1,2,3,4,5}
vector<int> Pi(string s) {
int n = s.size();
vector<int> result(n + 1);
result[0] = -1;
for (int i = 1; i < n; i++) {
for (int j = i - 1; j >= 0; j--) {
if (s.substr(0, j) == s.substr(i - j + 1, j)) {
result[i] = j;
break;
}
}
}
return result;
}
複雜度:O(n3)
超級慢
初始條件:
π[0]=−1,π[1]=0
Ps[πs[i+1]−1]∈Ps[i].CPS
$$$$$$$$$$$$$$$$$$ $…
Ps[i+1]
假設我們要找πs[i+1]
找πs[i+1]時
令j=1,2,…
如果Ps[i].CPS(j)的「下一個字元」==s[i+1]
則πs[i+1]=Ps[i].CPS(j)+1
$$$$$$$?$$$$$$$$$$?…
Ps[i+1]
s=abbacabbab…
πs={−1,0,0,0,1,0,1,2,3,4,?}
abbacabba b
s=abbacabbab…
πs={−1,0,0,0,1,0,1,2,3,4,?}
abbacabba b
Ps[9].CPS(1)=πs[9]=4
s=abbacabbab…
πs={−1,0,0,0,1,0,1,2,3,4,?}
abbacabba b
Ps[9].CPS(1)=πs[9]=4
s=abbacabbab…
πs={−1,0,0,0,1,0,1,2,3,4,?}
abbacabba b
Ps[9].CPS(2)=πs[πs[9]]=1
s=abbacabbab…
πs={−1,0,0,0,1,0,1,2,3,4,?}
abbacabba b
Ps[9].CPS(2)=πs[πs[9]]=1
s=abbacabbab…
πs={−1,0,0,0,1,0,1,2,3,4,?}
abbacabba b
πs[9+1]=1+1=2
現場打!
abbaabbab
abbaabbab
abbab
abbab
abbab
abbab
abbab
拿t往右推
t每次往右推一格
abbaabbab
abbab
abbab
abbab
abbab
abbab
拿t往右推
可不可以預知這兩個「壯志未酬身先死」?
t每次往右推一格
$$$$$$$$$$$$$$$$$$$ $
t[j]
$$$$$$$$$$$$$$ $
t[j−x]
證明當s[i]=t[j]時候:
那我們這個操作就可以將t向右推S中最小的
…$$$$$$$$$$$$$$$$$$$ $…
t往右推了x格
$$$$$$$$$$$$$$$$$$$ $
t[j]
$$$$$$$$$$$$$$ $
t[j−x]
證明當s[i]=t[j]時候:
那我們這個操作就可以將t向右推S中最小的
…$$$$$$$$$$$$$$$$$$$ $…
t往右推了x格
好啦其實就是CPS啦
$$$$$$$$$$$$$$$$$$$ $
t[j]
$$$$$$$$$$$$$$ $
t[j−x]
證明當s[i]=t[j]時候:
那我們這個操作就可以將t向右推S中最小的
…$$$$$$$$$$$$$$$$$$$ $…
t往右推了x格
好啦其實就是CPS啦
$$$$$$$$$$$$$$$$$$$ $
t[j]
$$$$$$$$$$$$$$ $
t[j−x]
假設j−x∈/Pt[j].CPS
且成功配到藍色格子
…$$$$$$$$$$$$$$$$$$$ $…
$$$$$$$$$$$$$$$$$$$ $
t[j]
$$$$$$$$$$$$$$ $
假設j−x∈/Pt[j].CPS
且成功配到藍色格子
…$$$$$$$$$$$$$$$$$$$ $…
t[j−x]
$$$$$$$$$$$$$$$$$$$ $
t[j]
$$$$$$$$$$$$$$ $
假設j−x∈/Pt[j].CPS
且成功配到藍色格子
…$$$$$$$$$$$$$$$$$$$ $…
則j−x∈Pt[j].CPS
⇒只有推x格的人可以活下來
t[j−x]
所以發現錯誤的時候
就找現在配到的字串的CPS(1) (π)
然後從剛剛錯的地方繼續
abbaabbab
abbab
abbab
abbab
abbabbab
abbab$
abbab
vector<int> StringMatching(string s, string t) {
int n = s.size(), m = t.size();
vector<int> pi = Pi(t), ans;
s.push_back('^');
t.push_back('$');
for (int i = 0, j = 0; i < n + 1; i++, j++) {
if (j == m) ans.push_back(i - m);
while (j >= 0 && s[i] != t[j]) j = pi[j];
}
return ans;
}
另一種想法
只要把t,$,s連起來
然後做π
然後數$後面哪些的π值是∣t∣
vector<int> stringMatching(string s, string t) {
int m = t.size();
vector<int> v;
vector<int> pi = Pi(t + "$" + s);
for (int i = 0; i < pi.size(); i++) {
if (pi[i] == m) v.push_back(i - m - m);
}
return v;
}
為什麼不先講這個…
前綴自動機
…abbaa…
abbab
abbab
abbab
優化再優化!
i | 'a' | 'b' |
---|---|---|
0 | 1 | 0 |
1 | 1 | 2 |
2 | 1 | 3 |
3 | 4 | 0 |
4 | 1 | 5 |
5 | 1 | 3 |
t=abbab
對t開一張PA表:
abbaabbab
i | 'a' | 'b' |
---|---|---|
0 | 1 | 0 |
1 | 1 | 2 |
2 | 1 | 3 |
3 | 4 | 0 |
4 | 1 | 5 |
5 | 1 | 3 |
abbab
abbaabbab
i | 'a' | 'b' |
---|---|---|
0 | 1 | 0 |
1 | 1 | 2 |
2 | 1 | 3 |
3 | 4 | 0 |
4 | 1 | 5 |
5 | 1 | 3 |
abbab
abbaabbab
i | 'a' | 'b' |
---|---|---|
0 | 1 | 0 |
1 | 1 | 2 |
2 | 1 | 3 |
3 | 4 | 0 |
4 | 1 | 5 |
5 | 1 | 3 |
abbab
abbaabbab
i | 'a' | 'b' |
---|---|---|
0 | 1 | 0 |
1 | 1 | 2 |
2 | 1 | 3 |
3 | 4 | 0 |
4 | 1 | 5 |
5 | 1 | 3 |
abbab
abbaabbab
i | 'a' | 'b' |
---|---|---|
0 | 1 | 0 |
1 | 1 | 2 |
2 | 1 | 3 |
3 | 4 | 0 |
4 | 1 | 5 |
5 | 1 | 3 |
abbab
abbaabbab
i | 'a' | 'b' |
---|---|---|
0 | 1 | 0 |
1 | 1 | 2 |
2 | 1 | 3 |
3 | 4 | 0 |
4 | 1 | 5 |
5 | 1 | 3 |
abbab
abbaabbab
i | 'a' | 'b' |
---|---|---|
0 | 1 | 0 |
1 | 1 | 2 |
2 | 1 | 3 |
3 | 4 | 0 |
4 | 1 | 5 |
5 | 1 | 3 |
abbab
abbaabbab
i | 'a' | 'b' |
---|---|---|
0 | 1 | 0 |
1 | 1 | 2 |
2 | 1 | 3 |
3 | 4 | 0 |
4 | 1 | 5 |
5 | 1 | 3 |
abbab
abbaabbab
i | 'a' | 'b' |
---|---|---|
0 | 1 | 0 |
1 | 1 | 2 |
2 | 1 | 3 |
3 | 4 | 0 |
4 | 1 | 5 |
5 | 1 | 3 |
abbab
abbaabbab
i | 'a' | 'b' |
---|---|---|
0 | 1 | 0 |
1 | 1 | 2 |
2 | 1 | 3 |
3 | 4 | 0 |
4 | 1 | 5 |
5 | 1 | 3 |
abbab
abbaabbab
i | 'a' | 'b' |
---|---|---|
0 | 1 | 0 |
1 | 1 | 2 |
2 | 1 | 3 |
3 | 4 | 0 |
4 | 1 | 5 |
5 | 1 | 3 |
abbab
abbaabbab
i | 'a' | 'b' |
---|---|---|
0 | 1 | 0 |
1 | 1 | 2 |
2 | 1 | 3 |
3 | 4 | 0 |
4 | 1 | 5 |
5 | 1 | 3 |
abbab
abbaabbab
i | 'a' | 'b' |
---|---|---|
0 | 1 | 0 |
1 | 1 | 2 |
2 | 1 | 3 |
3 | 4 | 0 |
4 | 1 | 5 |
5 | 1 | 3 |
abbab
abbaabbab
i | 'a' | 'b' |
---|---|---|
0 | 1 | 0 |
1 | 1 | 2 |
2 | 1 | 3 |
3 | 4 | 0 |
4 | 1 | 5 |
5 | 1 | 3 |
abbab
abbaabbab
i | 'a' | 'b' |
---|---|---|
0 | 1 | 0 |
1 | 1 | 2 |
2 | 1 | 3 |
3 | 4 | 0 |
4 | 1 | 5 |
5 | 1 | 3 |
abbab
abbaabbab
i | 'a' | 'b' |
---|---|---|
0 | 1 | 0 |
1 | 1 | 2 |
2 | 1 | 3 |
3 | 4 | 0 |
4 | 1 | 5 |
5 | 1 | 3 |
abbab
abbaabbab
i | 'a' | 'b' |
---|---|---|
0 | 1 | 0 |
1 | 1 | 2 |
2 | 1 | 3 |
3 | 4 | 0 |
4 | 1 | 5 |
5 | 1 | 3 |
abbab
abbaabbab
i | 'a' | 'b' |
---|---|---|
0 | 1 | 0 |
1 | 1 | 2 |
2 | 1 | 3 |
3 | 4 | 0 |
4 | 1 | 5 |
5 | 1 | 3 |
abbab
abbaabbab
i | 'a' | 'b' |
---|---|---|
0 | 1 | 0 |
1 | 1 | 2 |
2 | 1 | 3 |
3 | 4 | 0 |
4 | 1 | 5 |
5 | 1 | 3 |
abbab
abbaabbab
i | 'a' | 'b' |
---|---|---|
0 | 1 | 0 |
1 | 1 | 2 |
2 | 1 | 3 |
3 | 4 | 0 |
4 | 1 | 5 |
5 | 1 | 3 |
abbab
abbaabbab
i | 'a' | 'b' |
---|---|---|
0 | 1 | 0 |
1 | 1 | 2 |
2 | 1 | 3 |
3 | 4 | 0 |
4 | 1 | 5 |
5 | 1 | 3 |
abbab
abbaabbab
i | 'a' | 'b' |
---|---|---|
0 | 1 | 0 |
1 | 1 | 2 |
2 | 1 | 3 |
3 | 4 | 0 |
4 | 1 | 5 |
5 | 1 | 3 |
abbab
abbaabbab
i | 'a' | 'b' |
---|---|---|
0 | 1 | 0 |
1 | 1 | 2 |
2 | 1 | 3 |
3 | 4 | 0 |
4 | 1 | 5 |
5 | 1 | 3 |
abbab
abbaabbab
i | 'a' | 'b' |
---|---|---|
0 | 1 | 0 |
1 | 1 | 2 |
2 | 1 | 3 |
3 | 4 | 0 |
4 | 1 | 5 |
5 | 1 | 3 |
abbab
abbaabbab
i | 'a' | 'b' |
---|---|---|
0 | 1 | 0 |
1 | 1 | 2 |
2 | 1 | 3 |
3 | 4 | 0 |
4 | 1 | 5 |
5 | 1 | 3 |
abbab
abbaabbab
i | 'a' | 'b' |
---|---|---|
0 | 1 | 0 |
1 | 1 | 2 |
2 | 1 | 3 |
3 | 4 | 0 |
4 | 1 | 5 |
5 | 1 | 3 |
abbab
abbaabbab
i | 'a' | 'b' |
---|---|---|
0 | 1 | 0 |
1 | 1 | 2 |
2 | 1 | 3 |
3 | 4 | 0 |
4 | 1 | 5 |
5 | 1 | 3 |
abbab
abbaabbab
i | 'a' | 'b' |
---|---|---|
0 | 1 | 0 |
1 | 1 | 2 |
2 | 1 | 3 |
3 | 4 | 0 |
4 | 1 | 5 |
5 | 1 | 3 |
abbab
abbaabbab
i | 'a' | 'b' |
---|---|---|
0 | 1 | 0 |
1 | 1 | 2 |
2 | 1 | 3 |
3 | 4 | 0 |
4 | 1 | 5 |
5 | 1 | 3 |
abbab
vector<vector<int>> PrefixAutomaton(string s) {
int n = s.size();
vector<vector<int>> dp(n + 1, vector<int>(26, 0));
vector<int> pi = Pi(s);
s += '$';
dp[0][s[0] - 'a'] = 1;
for (int i = 1; i <= n; i++) for (int j = i; j >= 0; j = pi[j]) if (j < n) dp[i][s[j] - 'a'] = max(dp[i][s[j] - 'a'], j + 1);
return dp;
}
vector<int> StringMatching(string s, string t) {
int n = s.size(), m = t.size();
auto PA = PrefixAutomaton(t);
vector<int> ans;
for (int i = 0, j = 0; i < n; i++) {
j = PA[j][s[i] - 'a'];
if (j == m) ans.push_back(i - m + 1);
}
return ans;
}
建表:O(n)
配對:O(1)/每個字元
給定三字串s0,s1,t和一整數n
令si+1=5×si+6×si−1,i∈Z+
求在sn裡面可以找到幾個t
給定三字串s0,s1,t和一整數n
令si+1=5×si+6×si−1,i∈Z+
求在sn裡面可以找到幾個t
顯然地
∣sn∣=71{[6×(−1)n+6n]∣s0∣+[6n−(−1)n]∣s1∣}
直接用π會炸
直接用PA也會炸
i | 'a' | 'b' | "bab" |
---|---|---|---|
0 | 1 | 0 | |
1 | 1 | 2 | |
2 | 1 | 3 | |
3 | 4 | 0 | |
4 | 1 | 5 | |
5 | 1 | 3 |
t=abbab
i | 'a' | 'b' | "bab" |
---|---|---|---|
0 | 1 | 0 | |
1 | 1 | 2 | |
2 | 1 | 3 | |
3 | 4 | 0 | |
4 | 1 | 5 | |
5 | 1 | 3 |
t=abbab
i | 'a' | 'b' | "bab" |
---|---|---|---|
0 | 1 | 0 | |
1 | 1 | 2 | |
2 | 1 | 3 | |
3 | 4 | 0 | |
4 | 1 | 5 | |
5 | 1 | 3 |
t=abbab
i | 'a' | 'b' | "bab" |
---|---|---|---|
0 | 1 | 0 | |
1 | 1 | 2 | |
2 | 1 | 3 | |
3 | 4 | 0 | |
4 | 1 | 5 | |
5 | 1 | 3 |
t=abbab
i | 'a' | 'b' | "bab" |
---|---|---|---|
0 | 1 | 0 | |
1 | 1 | 2 | |
2 | 1 | 3 | |
3 | 4 | 0 | |
4 | 1 | 5 | |
5 | 1 | 3 |
t=abbab
i | 'a' | 'b' | "bab" |
---|---|---|---|
0 | 1 | 0 | |
1 | 1 | 2 | |
2 | 1 | 3 | |
3 | 4 | 0 | |
4 | 1 | 5 | |
5 | 1 | 3 |
t=abbab
i | 'a' | 'b' | "bab" |
---|---|---|---|
0 | 1 | 0 | |
1 | 1 | 2 | |
2 | 1 | 3 | |
3 | 4 | 0 | |
4 | 1 | 5 | |
5 | 1 | 3 |
t=abbab
i | 'a' | 'b' | "bab" |
---|---|---|---|
0 | 1 | 0 | 2 |
1 | 1 | 2 | |
2 | 1 | 3 | |
3 | 4 | 0 | |
4 | 1 | 5 | |
5 | 1 | 3 |
t=abbab
i | 'a' | 'b' | "bab" |
---|---|---|---|
0 | 1 | 0 | 2 |
1 | 1 | 2 | |
2 | 1 | 3 | |
3 | 4 | 0 | |
4 | 1 | 5 | |
5 | 1 | 3 |
t=abbab
i | 'a' | 'b' | "bab" |
---|---|---|---|
0 | 1 | 0 | 2 |
1 | 1 | 2 | |
2 | 1 | 3 | |
3 | 4 | 0 | |
4 | 1 | 5 | |
5 | 1 | 3 |
t=abbab
i | 'a' | 'b' | "bab" |
---|---|---|---|
0 | 1 | 0 | 2 |
1 | 1 | 2 | |
2 | 1 | 3 | |
3 | 4 | 0 | |
4 | 1 | 5 | (+1) |
5 | 1 | 3 |
t=abbab
i | 'a' | 'b' | "bab" |
---|---|---|---|
0 | 1 | 0 | 2 |
1 | 1 | 2 | |
2 | 1 | 3 | |
3 | 4 | 0 | |
4 | 1 | 5 | (+1) |
5 | 1 | 3 |
t=abbab
i | 'a' | 'b' | "bab" |
---|---|---|---|
0 | 1 | 0 | 2 |
1 | 1 | 2 | |
2 | 1 | 3 | |
3 | 4 | 0 | |
4 | 1 | 5 | (+1) |
5 | 1 | 3 |
t=abbab
i | 'a' | 'b' | "bab" |
---|---|---|---|
0 | 1 | 0 | 2 |
1 | 1 | 2 | |
2 | 1 | 3 | |
3 | 4 | 0 | |
4 | 1 | 5 | (+1) |
5 | 1 | 3 |
t=abbab
i | 'a' | 'b' | "bab" |
---|---|---|---|
0 | 1 | 0 | 2 |
1 | 1 | 2 | |
2 | 1 | 3 | |
3 | 4 | 0 | |
4 | 1 | 5 | (+1) |
5 | 1 | 3 |
t=abbab
i | 'a' | 'b' | "bab" |
---|---|---|---|
0 | 1 | 0 | 2 |
1 | 1 | 2 | |
2 | 1 | 3 | |
3 | 4 | 0 | |
4 | 1 | 5 | 2 (+1) |
5 | 1 | 3 |
t=abbab
i | 'a' | 'b' | "bab" |
---|---|---|---|
0 | 1 | 0 | 2 |
1 | 1 | 2 | 2 |
2 | 1 | 3 | 5 (+1) |
3 | 4 | 0 | 2 |
4 | 1 | 5 | 2 (+1) |
5 | 1 | 3 | 5 (+1) |
t=abbab
一台自動機=⟨Q,Σ,δ,q0,F⟩
=⟨狀態,字母表,轉移函式,初始狀態,終止狀態⟩
Q=圈圈們
Σ={a,b}
δ=箭頭們
q0=橘色圈圈
F=綠色圈圈們
走走看:abbaabbab
t=abbab
走走看:abbaabbab
δ(q,c)=
在q所代表的字串+c中,
有出現在樹上的最長後綴
δ(q,c)=
在q所代表的字串+c中,
有出現在樹上的最長後綴
δ(q,c)=
在q所代表的字串+c中,
有出現在樹上的最長後綴
δ(q,c)=
在q所代表的字串+c中,
有出現在樹上的最長後綴
走走看:abbaabbab
初始條件:
q[0].SIT(1)=nullptr
q[i].SIT(1)=q[0]
∀q[i]∈depth(1)
$$$$$$$$$ $
藍色∈Trie
假設紅色為qi.SIT(1)
則紅色∈Trie
則綠色∈Trie
則綠色∈藍色的SIT
qi
qi
找q[i].SIT(1)時
令j=1,2,…
如果q[i].father.SIT(j)有辦法走q[i].lastChar
則q[i].SIT(1)=q[i].father.SIT(j).next[q[i].lastChar]
$$$$$$$$$ $
如果他有辦法往a走的話
那個a就會是SIT
但它顯然沒有
好耶
做它的SL
好耶
走走看:abbaabbab
在abbabbab中匹配下列字串:
abbab
bab
ba
在abbabbab中匹配下列字串:
abbab
bab
ba
在abbabbab中匹配下列字串:
abbab
bab
ba
在abbabbab中匹配下列字串:
abbab
bab
ba
為什麼配不到bab和ba?
struct TrieNode {
int id;
char lastChar;
TrieNode *next[26];
TrieNode *SL;
TrieNode *AL;
TrieNode(TrieNode *parent = nullptr, char _lastChar = '\0') {
id = -1;
lastChar = _lastChar;
fill(next, next + 26, nullptr);
SL = parent;
AL = nullptr;
}
void push(string s, int _id) {
TrieNode *now = this;
for (auto &i : s) {
if (now -> next[i - 'a'] == nullptr) now -> next[i - 'a'] = new TrieNode(now, i);
now = now -> next[i - 'a'];
}
now -> id = _id;
}
void construct() {
queue<TrieNode*> q;
for (auto &i : next) {
if (i == nullptr) continue;
q.push(i);
}
while (q.size()) {
TrieNode *now = q.front();
q.pop();
for (auto &i : now -> next) {
if (i == nullptr) continue;
q.push(i);
}
TrieNode *back = now -> SL -> SL;
while (back && back -> next[now -> lastChar - 'a'] == nullptr) back = back -> SL;
if (back) now -> SL = back -> next[now -> lastChar - 'a'];
else now -> SL = this;
if (now -> SL -> id != -1) now -> AL = now -> SL;
else now -> AL = now -> SL -> AL;
}
}
};
struct AC {
vector<string> s;
TrieNode *root;
AC(vector<string> _s) {
s = _s;
root = new TrieNode();
}
void push(string _s) {
s.push_back(_s);
}
void construct() {
int n = s.size();
for (int i = 0; i < n; i++) root -> push(s[i], i);
root -> construct();
}
vector<int> stringMatching(string _s) {
int n = s.size();
vector<int> ans(n);
TrieNode *now = root;
for (auto &i : _s) {
while (now && now -> next[i - 'a'] == nullptr) now = now -> SL;
if (now) now = now -> next[i - 'a'];
else now = root;
if (now -> id != -1) ans[now -> id]++;
TrieNode *back = now -> AL;
while (back) {
ans[back -> id]++;
back = back -> AL;
}
}
return ans;
}
};
s=abbabbab
zs={_,0,0,5,0,0,2,0}
對t+"$"+s做z
就好了
zzz
$$$$$$A$$$$$$$$$B$$$…
令l=jargmax(j+zs[j])
現在已經配到最右邊的那個人
我們現在要找zs[i]
根據i的位置,可以分成2種情況:
l
zs[l]
l+zs[l]
$$$$$$A$$$$$$$$$B$$$…
l
zs[l]
l+zs[l]
這個時候我們令i′=i−l
即Prefix中對應到的i
i
$$$$$$A$$$$$$$$$B$$$…
l
zs[l]
l+zs[l]
這個時候我們令i′=i−l
即Prefix中對應到的i
i
i′
$$C$$DA$$$$$$$$$B$$$…
1-1. i′+zs[i′]<zs[l]
l
zs[l]
l+zs[l]
i
i′
$$C$$DA$$$$$C$$DB$$$…
1-1. i′+zs[i′]<zs[l]
l
zs[l]
l+zs[l]
i
i′
$$C$$DA$$$$$C$$DB$$$…
1-1. i′+zs[i′]<zs[l]
l
zs[l]
l+zs[l]
i
i′
⇒zs[i]=zs[i′]
$$$C$$A$$$$$$$$$B$$$…
1-2. i′+zs[i′]=zs[l]
l
zs[l]
l+zs[l]
i
i′
$$$C$$A$$$$$$C$$B$$$…
1-2. i′+zs[i′]=zs[l]
l
zs[l]
l+zs[l]
i
i′
$$$C$$A$$$$$$C$$B$$$…
1-2. i′+zs[i′]=zs[l]
l
zs[l]
l+zs[l]
i
i′
A=B
C=B
A=C?
$$$C$$A$$$$$$C$$B$$$…
1-2. i′+zs[i′]=zs[l]
l
zs[l]
l+zs[l]
i
i′
所以這個時候
只能確定zs[i]≥zs[i′]
那就繼續配下去
$$$$C$AD$$$$$$$$B$$$…
1-3. i′+zs[i′]>zs[l]
l
zs[l]
l+zs[l]
i
i′
$$$$C$AD$$$$$$C$B$$$…
1-3. i′+zs[i′]>zs[l]
l
zs[l]
l+zs[l]
i
i′
$$$$C$AD$$$$$$C$B$$$…
1-3. i′+zs[i′]>zs[l]
l
zs[l]
l+zs[l]
i
i′
A=B
$$$$C$AD$$$$$$C$B$$$…
1-3. i′+zs[i′]>zs[l]
l
zs[l]
l+zs[l]
i
i′
⇒zs[i]=zs[l]−i′
$$$$$$A$$$$$$$$$B$$$…
2. i≥l+zs[l]
l
zs[l]
l+zs[l]
i
$$$$$$A$$$$$$$$$B$$$…
l
zs[l]
l+zs[l]
i
沒料
只好從頭配了
2. i≥l+zs[l]
vector<int> Z(string s) {
int n = s.size();
vector<int> z(n);
z[0] = 0;
int l = 0;
for (int i = 1; i < n; i++) {
if (i >= l + z[l]) { // 2.
for (z[i] = 0; i + z[i] < n && s[z[i]] == s[i + z[i]]; z[i]++);
l = i;
continue;
}
int i_ = i - l;
if (i_ + z[i_] < z[l]) z[i] = z[i_]; // 1-1.
else if (i_ + z[i_] > z[l]) z[i] = z[l] - i_; // 1-3.
else { // 1-2.
for (z[i] = i_; i + z[i] < n && s[z[i]] == s[i + z[i]]; z[i]++);
if (z[i] > z[l]) l = i;
}
}
return z;
}
int stringMatching(string s, string t) {
int n = s.size(), m = t.size(), C = 0;
vector<int> z = Z(t + "$" + s);
for (int i = m + 1; i < n + m + 1; i++) {
if (z[i] == t.size()) C++;
}
return C;
}
終於要用到PD了!
abbab
∗a∗b∗b∗a∗b∗
s=∗a∗b∗b∗a∗b∗
zs={0,1,0,1,4,1,0,3,0,1,0}
∑ms[i]
就好了
zzz
$$A$$$$$$$$$$$$$B$$$…
我們現在要找ms[i]
令l=j<iargmax(j+ms[j])
展開雙翼(?)後最右邊的人
根據i的位置,可以分成2種情況:
l
l−ms[l]
l+ms[l]
$$A$$$$$$$$$$$$$B$$$…
1. i≤l+ms[l]
l
l−ms[l]
l+ms[l]
i
這個時候我們令i′=l−(i−l)
即以s[l]對稱過去的i
$$A$$$$$$$$$$$$$B$$$…
1. i≤l+ms[l]
l
l−ms[l]
l+ms[l]
i
這個時候我們令i′=l−(i−l)
即以s[l]對稱過去的i
i′
$$AC$$$$$D$$$$$$B$$$…
1-1. i′−ms[i′]>l−ms[l]
l
l−ms[l]
l+ms[l]
i
i′
$$AC$$$$$D$$$$$CB$$$…
1-1. i′−ms[i′]>l−ms[l]
l
l−ms[l]
l+ms[l]
i
i′
$$AC$$$$$D$$$$$CB$$$…
1-1. i′−ms[i′]>l−ms[l]
l
l−ms[l]
l+ms[l]
i
i′
⇒ms[i]=ms[i′]
$$A$$$$$$$C$$$$$B$$$…
1-2. i′−ms[i′]=l−ms[l]
l
l−ms[l]
l+ms[l]
i
i′
$$A$$$$$C$C$$$$$B$$$…
1-2. i′−ms[i′]=l−ms[l]
l
l−ms[l]
l+ms[l]
i
i′
$$A$$$$$C$C$$$$$B$$$…
1-2. i′−ms[i′]=l−ms[l]
l
l−ms[l]
l+ms[l]
i
i′
A=B
C=B
A=C?
$$A$$$$$C$C$$$$$B$$$…
1-2. i′−ms[i′]=l−ms[l]
l
l−ms[l]
l+ms[l]
i
i′
所以這個時候
只能確定ms[i]≥ms[i′]
那就繼續配下去
$CA$$$$$$$$D$$$$B$$$…
1-3. i′−ms[i′]<l−ms[l]
l
l−ms[l]
l+ms[l]
i
i′
$CA$$$$D$$$D$$$$B$$…
1-3. i′−ms[i′]<l−ms[l]
l
l−ms[l]
l+ms[l]
i
i′
$CA$$$$D$$$D$$$$B$$…
1-3. i′−ms[i′]<l−ms[l]
l
l−ms[l]
l+ms[l]
i
i′
A=B
$CA$$$$D$$$D$$$$B$$…
1-3. i′−ms[i′]<l−ms[l]
l
l−ms[l]
l+ms[l]
i
i′
⇒ms[i]=i′−l+ms[l]
$$A$$$$$$$$$$$$$B$$$…
2. i>l+ms[l]
l
l−ms[l]
l+ms[l]
i
$$A$$$$$$$$$$$$$B$$$…
2. i>l+ms[l]
l
l−ms[l]
l+ms[l]
i
沒料
只好從頭配了
vector<int> M(string s) {
string br = "*";
for (auto &i : s) br += string(1, s[i]) + "*";
s = br;
int n = s.size();
vector<int> m(n);
m[0] = 0;
int l = 0;
for (int i = 1; i < n; i++) {
if (i > l + m[l]) {
for (m[i] = 0; i - m[i] - 1 >= 0 && i + m[i] + 1 < n && s[i - m[i] - 1] == s[i + m[i] + 1]; m[i]++);
l = i;
continue;
}
int i_ = l - i + l;
if (i_ - m[i_] > l - m[l]) m[i] = m[i_];
else if (i_ - m[i_] < l - m[l]) m[i] = i_ - l + m[l];
else {
for (m[i] = m[i_]; i - m[i] - 1 >= 0 && i + m[i] + 1 < n && s[i - m[i] - 1] == s[i + m[i] + 1]; m[i]++);
if (i + m[i] > l + m[l]) l = i;
}
}
return m;
}
s=abbab
Ss[0]=\0
Ss[1]=b
Ss[2]=ab
Ss[3]=bab
Ss[4]=bbab
Ss[5]=abbab
Ss[1]=b
Ss[2]=ab
Ss[3]=bab
Ss[4]=bbab
Ss[5]=abbab
Ss[0]=\0
s=abbab
Ss[0]=\0
Ss[1]=b
Ss[2]=ab
Ss[3]=bab
Ss[4]=bbab
Ss[5]=abbab
Ss[1]=b
Ss[2]=ab
Ss[3]=bab
Ss[4]=bbab
Ss[5]=abbab
Ss[0]=\0
sort
s=abbab
Ss[0]=\0
Ss[1]=b
Ss[2]=ab
Ss[3]=bab
Ss[4]=bbab
Ss[5]=abbab
sort
サ={0,2,5,1,3,4}
vector<int> SA(string s) {
int n = s.size();
vector<int> sa(n + 1);
vector<pair<string, int>> v(n + 1);
for (int i = 0; i <= n; i++) v[i] = {s.substr(i, n - i), i};
sort(v.begin(), v.end());
for (int i = 0; i <= n; i++) sa[i] = v[i].second;
return sa;
}
O(n2logn)
s.cycle(0)=abbab\0
s.cycle(3)=ab\0abb
s.cycle(4)=b\0abba
s.cycle(5)=\0abbab
s.cycle(1)=bbab\0a
s.cycle(2)=bab\0ab
s.cycle(0)=abbab\0
s.cycle(3)=ab\0abb
s.cycle(4)=b\0abba
s.cycle(5)=\0abbab
s.cycle(1)=bbab\0a
s.cycle(2)=bab\0ab
s.cycle(0)=abbab\0
s.cycle(3)=ab\0abb
s.cycle(4)=b\0abba
s.cycle(5)=\0abbab
s.cycle(1)=bbab\0a
s.cycle(2)=bab\0ab
s.cycle(0)=abbab\0
s.cycle(3)=ab\0abb
s.cycle(4)=b\0abba
s.cycle(5)=\0abbab
s.cycle(1)=bbab\0a
s.cycle(2)=bab\0ab
sort
s.cycle(0)=abbab\0
s.cycle(3)=ab\0abb
s.cycle(4)=b\0abba
s.cycle(5)=\0abbab
s.cycle(1)=bbab\0a
s.cycle(2)=bab\0ab
sort
サ={5,3,0,4,2,1}
我們其實比較喜歡這個排序
Cyc | string | rank |
---|---|---|
0 | abbab\0 | |
1 | bbab\0a | |
2 | bab\0ab | |
3 | ab\0abb | |
4 | b\0abba | |
5 | \0abbab |
Cyc | string | rank |
---|---|---|
0 | abbab\0 | |
1 | bbab\0a | |
2 | bab\0ab | |
3 | ab\0abb | |
4 | b\0abba | |
5 | \0abbab |
0a
Text
1b
Text
2b
Text
3a
Text
4b
Text
5\0
Text
Cyc | string | rank |
---|---|---|
0 | abbab\0 | |
1 | bbab\0a | |
2 | bab\0ab | |
3 | ab\0abb | |
4 | b\0abba | |
5 | \0abbab |
0a
Text
1b
Text
2b
Text
3a
Text
4b
Text
5\0
Text
Cyc | string | rank |
---|---|---|
0 | abbab\0 | |
1 | bbab\0a | |
2 | bab\0ab | |
3 | ab\0abb | |
4 | b\0abba | |
5 | \0abbab |
0a
1
1b
3
2b
3
3a
1
4b
3
5\0
0
Cyc | string | rank |
---|---|---|
0 | abbab\0 | 1 |
1 | bbab\0a | 3 |
2 | bab\0ab | 3 |
3 | ab\0abb | 1 |
4 | b\0abba | 3 |
5 | \0abbab | 0 |
0a
1
1b
3
2b
3
3a
1
4b
3
5\0
0
Cyc | string | rank |
---|---|---|
0 | abbab\0 | 1 |
1 | bbab\0a | 3 |
2 | bab\0ab | 3 |
3 | ab\0abb | 1 |
4 | b\0abba | 3 |
5 | \0abbab | 0 |
0a b
1
1b b
3
2b a
3
3a b
1
4b \0
3
5\0 a
0
Cyc | string | rank |
---|---|---|
0 | abbab\0 | 1 |
1 | bbab\0a | 3 |
2 | bab\0ab | 3 |
3 | ab\0abb | 1 |
4 | b\0abba | 3 |
5 | \0abbab | 0 |
0a b
1, 3
1b b
3, 3
2b a
3, 1
3a b
1, 3
4b \0
3, 0
5\0 a
0, 1
Cyc | string | rank |
---|---|---|
0 | abbab\0 | 1 |
1 | bbab\0a | 3 |
2 | bab\0ab | 3 |
3 | ab\0abb | 1 |
4 | b\0abba | 3 |
5 | \0abbab | 0 |
0a b
1, 3
1b b
3, 3
2b a
3, 1
3a b
1, 3
4b \0
3, 0
5\0 a
0, 1
Cyc | string | rank |
---|---|---|
0 | abbab\0 | 1 |
1 | bbab\0a | 3 |
2 | bab\0ab | 3 |
3 | ab\0abb | 1 |
4 | b\0abba | 3 |
5 | \0abbab | 0 |
0ab
1
1bb
5
2ba
4
3ab
1
4b\0
3
5\0a
0
Cyc | string | rank |
---|---|---|
0 | abbab\0 | 1 |
1 | bbab\0a | 5 |
2 | bab\0ab | 4 |
3 | ab\0abb | 1 |
4 | b\0abba | 3 |
5 | \0abbab | 0 |
0ab
1
1bb
5
2ba
4
3ab
1
4b\0
3
5\0a
0
Cyc | string | rank |
---|---|---|
0 | abbab\0 | 1 |
1 | bbab\0a | 5 |
2 | bab\0ab | 4 |
3 | ab\0abb | 1 |
4 | b\0abba | 3 |
5 | \0abbab | 0 |
0ab ba
1
1bb ab
5
2ba b\0
4
3ab \0a
1
4b\0 ab
3
5\0a bb
0
Cyc | string | rank |
---|---|---|
0 | abbab\0 | 1 |
1 | bbab\0a | 5 |
2 | bab\0ab | 4 |
3 | ab\0abb | 1 |
4 | b\0abba | 3 |
5 | \0abbab | 0 |
0ab ba
1, 4
1bb ab
5, 1
2ba b\0
4, 3
3ab \0a
1, 0
4b\0 ab
3, 1
5\0a bb
0, 5
Cyc | string | rank |
---|---|---|
0 | abbab\0 | 1 |
1 | bbab\0a | 5 |
2 | bab\0ab | 4 |
3 | ab\0abb | 1 |
4 | b\0abba | 3 |
5 | \0abbab | 0 |
0ab ba
1, 4
1bb ab
5, 1
2ba b\0
4, 3
3ab \0a
1, 0
4b\0 ab
3, 1
5\0a bb
0, 5
Cyc | string | rank |
---|---|---|
0 | abbab\0 | 1 |
1 | bbab\0a | 5 |
2 | bab\0ab | 4 |
3 | ab\0abb | 1 |
4 | b\0abba | 3 |
5 | \0abbab | 0 |
0abba
2
1bbab
5
2bab\0
4
3ab\0a
1
4b\0ab
3
5\0abb
0
Cyc | string | rank |
---|---|---|
0 | abbab\0 | 2 |
1 | bbab\0a | 5 |
2 | bab\0ab | 4 |
3 | ab\0abb | 1 |
4 | b\0abba | 3 |
5 | \0abbab | 0 |
0abba
2
1bbab
5
2bab\0
4
3ab\0a
1
4b\0ab
3
5\0abb
0
Cyc | string | rank |
---|---|---|
0 | abbab\0 | 2 |
1 | bbab\0a | 5 |
2 | bab\0ab | 4 |
3 | ab\0abb | 1 |
4 | b\0abba | 3 |
5 | \0abbab | 0 |
0abba b\0ab
2
1bbab \0abb
5
2bab\0 abba
4
3ab\0a bbab
1
4b\0ab bab\0
3
5\0abb ab\0a
0
Cyc | string | rank |
---|---|---|
0 | abbab\0 | 2 |
1 | bbab\0a | 5 |
2 | bab\0ab | 4 |
3 | ab\0abb | 1 |
4 | b\0abba | 3 |
5 | \0abbab | 0 |
0abbab\0ab
2
1bbab\0abb
5
2bab\0abba
4
3ab\0abbab
1
4b\0abbab\0
3
5\0abbab\0a
0
Cyc | string | rank |
---|---|---|
0 | abbab\0 | 2 |
1 | bbab\0a | 5 |
2 | bab\0ab | 4 |
3 | ab\0abb | 1 |
4 | b\0abba | 3 |
5 | \0abbab | 0 |
0abbab\0ab
2
1bbab\0abb
5
2bab\0abba
4
3ab\0abbab
1
4b\0abbab\0
3
5\0abbab\0a
0
將每個字串往後抄
目前長度個字元
把兩段字串分別用rank值表示
弄出一個pair
利用pair排序
把新的rank抄回去
將每個字串往後抄
目前長度個字元
把兩段字串分別用rank值表示
弄出一個pair
利用pair排序
把新的rank抄回去
總共要繞logn圈
將每個字串往後抄
目前長度個字元
把兩段字串分別用rank值表示
弄出一個pair
利用pair排序
把新的rank抄回去
總共要繞logn圈
O(n)
O(nlogn)
O(n)
將每個字串往後抄
目前長度個字元
把兩段字串分別用rank值表示
弄出一個pair
利用pair排序
把新的rank抄回去
總共要繞logn圈
O(n)
O(nlogn)
O(n)
總複雜度O(nlog2n)
struct P {
pii p;
int id;
};
vector<int> SA(string s) {
int n = s.size();
vector<int> r(n + 1);
vector<P> v(n + 1);
vector<vector<P>> bar(n + 1);
function<bool(P, P)> cmp = [](P a, P b) {
return a.p < b.p;
};
function<void(void)> GetRank = [&]() {
for (int i = 0, j = 0; i <= n; i = j) {
while (j <= n && v[i].p == v[j].p) j++;
for (int k = i; k < j; k++) {
v[k].p.first = i;
r[v[k].id] = i;
}
}
};
for (int i = 0; i < n; i++) v[i] = {{s[i] - 'a' + 1, 0}, i};
v[n] = {{0, 0}, n};
sort(v.begin(), v.end(), [](P a, P b){return a.p < b.p;});
GetRank();
int len = 1;
while (len <= n) {
for (auto &i : v) i.p.second = r[(i.id + len) % (n + 1)];
sort(v.begin(), v.end(), cmp);
GetRank();
len <<= 1;
}
return r;
}
排序下列數字:
{41,78,38,57,59,50,72,43,46,61}
排序下列數字:
{41,78,38,57,59,50,72,43,46,61}
0 | |||
1 | |||
2 | |||
3 | |||
4 | |||
5 | |||
6 | |||
7 | |||
8 | |||
9 |
排序下列數字:
{41,78,38,57,59,50,72,43,46,61}
0 | 50 | ||
1 | 41 | 61 | |
2 | 72 | ||
3 | 43 | ||
4 | |||
5 | |||
6 | 46 | ||
7 | 57 | ||
8 | 78 | 38 | |
9 | 59 |
{41,78,38,57,59,50,72,43,46,61}
⇒{50,41,61,72,43,46,57,78,38,59}
0 | 50 | ||
1 | 41 | 61 | |
2 | 72 | ||
3 | 43 | ||
4 | |||
5 | |||
6 | 46 | ||
7 | 57 | ||
8 | 78 | 38 | |
9 | 59 |
{41,78,38,57,59,50,72,43,46,61}
⇒{50,41,61,72,43,46,57,78,38,59}
0 | |||
1 | |||
2 | |||
3 | |||
4 | |||
5 | |||
6 | |||
7 | |||
8 | |||
9 |
{41,78,38,57,59,50,72,43,46,61}
⇒{50,41,61,72,43,46,57,78,38,59}
0 | |||
1 | |||
2 | |||
3 | 38 | ||
4 | 41 | 43 | 46 |
5 | 50 | 57 | 59 |
6 | 61 | ||
7 | 72 | 78 | |
8 | |||
9 |
{41,78,38,57,59,50,72,43,46,61}
⇒{50,41,61,72,43,46,57,78,38,59}
⇒{38,41,43,46,50,57,59,61,72,78}
0 | |||
1 | |||
2 | |||
3 | 38 | ||
4 | 41 | 43 | 46 |
5 | 50 | 57 | 59 |
6 | 61 | ||
7 | 72 | 78 | |
8 | |||
9 |
將每個字串往後抄
目前長度個字元
把兩段字串分別用rank值表示
弄出一個pair
利用pair排序
把新的rank抄回去
總共要繞logn圈
O(n)
O(nlogn)
O(n)
將每個字串往後抄
目前長度個字元
把兩段字串分別用rank值表示
弄出一個pair
利用pair排序
把新的rank抄回去
總共要繞logn圈
O(n)
O(n)
O(n)
將每個字串往後抄
目前長度個字元
把兩段字串分別用rank值表示
弄出一個pair
利用pair排序
把新的rank抄回去
總共要繞logn圈
O(n)
O(n)
O(n)
總複雜度O(nlogn)
struct P {
pii p;
int id;
};
vector<int> SA(string s) {
int n = s.size();
vector<int> r(n + 1);
vector<P> v(n + 1);
vector<vector<P>> bar(n + 1);
function<void(void)> RS = [&]() {
for (auto &i : v) bar[i.p.second].push_back(i);
for (int i = 0, j = 0; i <= n; i++) {
for (auto &k : bar[i]) v[j++] = k;
bar[i].clear();
}
for (auto &i : v) bar[i.p.first].push_back(i);
for (int i = 0, j = 0; i <= n; i++) {
for (auto &k : bar[i]) v[j++] = k;
bar[i].clear();
}
};
function<void(void)> GetRank = [&]() {
for (int i = 0, j = 0; i <= n; i = j) {
while (j <= n && v[i].p == v[j].p) j++;
for (int k = i; k < j; k++) {
v[k].p.first = i;
r[v[k].id] = i;
}
}
};
for (int i = 0; i < n; i++) v[i] = {{s[i] - 'a' + 1, 0}, i};
v[n] = {{0, 0}, n};
sort(v.begin(), v.end(), [](P a, P b){return a.p < b.p;});
GetRank();
int len = 1;
while (len <= n) {
for (auto &i : v) i.p.second = r[(i.id + len) % (n + 1)];
RS();
GetRank();
len <<= 1;
}
return r;
}
LCP(abbab,abab)=2
abbab
abab
生出一個資料結構,可以幫我們查詢任意兩組後綴的LCP
生出一個資料結構,可以幫我們查詢任意兩組後綴的LCP
s=abbab\0
Ss[0]=\0
Ss[1]=b\0
Ss[2]=ab\0
Ss[3]=bab\0
Ss[4]=bbab\0
Ss[5]=abbab\0
LCPs(1,4)
=LCP(b,bbab)
=1
生出一個資料結構,可以幫我們查詢任意兩組後綴的LCP
好啦我們還是比較喜歡cycle
生出一個資料結構,可以幫我們查詢任意兩個cycle的LCP
生出一個資料結構,可以幫我們查詢任意兩個cycle的LCP
s=abbab\0
s.cycle(0)=abbab\0
s.cycle(1)=bbab\0a
s.cycle(2)=bab\0ab
s.cycle(3)=ab\0abb
s.cycle(4)=b\0abba
s.cycle(5)=\0abbab
LCPs(4,1)
=LCP(b\0abba,bbab\0a)
=1
還記得剛剛的rank陣列嗎
string | a | b | b | a | b | \0 |
rank | 1 | 3 | 3 | 1 | 3 | 0 |
string | ab | bb | bb | ab | b\0 | \0a |
rank | 1 | 5 | 4 | 1 | 3 | 0 |
string | abba | bbab | bab\0 | ab\0a | b\0ab | \0abb |
rank | 2 | 5 | 4 | 1 | 3 | 0 |
string | 放 | 不 | 下 | 了 | ... | ... |
rank | 2 | 5 | 4 | 1 | 3 | 0 |
但是空間複雜度呢?
Ss[5]=\0
Ss[3]=ab\0
Ss[0]=abbab\0
Ss[4]=b\0
Ss[2]=bab\0
Ss[1]=bbab\0
s=abbab
我們開一個陣列l
使得l[i]=排名在第i的後綴和排名在第i+1的後綴做LCP
Ss[5]=\0
Ss[3]=ab\0
Ss[0]=abbab\0
Ss[4]=b\0
Ss[2]=bab\0
Ss[1]=bbab\0
s=abbab
我們開一個陣列l
使得l[i]=排名在第i的後綴和排名在第i+1的後綴做LCP
l[0]=0
l[1]=2
l[2]=0
l[3]=1
l[4]=1
Ss[5]=\0
Ss[3]=ab\0
Ss[0]=abbab\0
Ss[4]=b\0
Ss[2]=bab\0
Ss[1]=bbab\0
l[0]=0
l[1]=2
l[2]=0
l[3]=1
l[4]=1
如果我想要找LCP(Ss[3],Ss[2])
Ss[5]=\0
Ss[3]=ab\0
Ss[0]=abbab\0
Ss[4]=b\0
Ss[2]=bab\0
Ss[1]=bbab\0
l[0]=0
l[1]=2
l[2]=0
l[3]=1
l[4]=1
如果我想要找LCP(Ss[3],Ss[2])
答案會是min(l[1…4])
找LCP會變成求區間最小值(RMQ)
拿棵線段樹什麼的維護l就好了
s=abbab
50
3ab0
0abbab0
4b0
2bab0
1bbab0
l[0]=?
l[1]=?
l[2]=?
l[3]=?
l[4]=?
s=abbab
50
3ab0
0abbab0
4b0
2bab0
1bbab0
l[0]=?
l[1]=?
l[2]=?
l[3]=?
l[4]=?
s=abbab
50
3ab0
0abbab0
4b0
2bab0
1bbab0
l[0]=?
l[1]=?
l[2]=?
l[3]=?
l[4]=?
s=abbab
50
3ab0
0abbab0
4b0
2bab0
1bbab0
l[0]=?
l[1]=?
l[2]=?
l[3]=?
l[4]=?
s=abbab
50
3ab0
0abbab0
4b0
2bab0
1bbab0
l[0]=?
l[1]=?
l[2]=?
l[3]=?
l[4]=?
s=abbab
50
3ab0
0abbab0
4b0
2bab0
1bbab0
l[0]=?
l[1]=2
l[2]=?
l[3]=?
l[4]=?
s=abbab
50
3ab0
0abbab0
4b0
2bab0
1bbab0
l[0]=?
l[1]=2
l[2]=?
l[3]=?
l[4]=?
s=abbab
50
3ab0
0abbab0
4b0
2bab0
1bbab0
l[0]=?
l[1]=2
l[2]=?
l[3]=?
l[4]=?
LCP(Ss[0],Ss[3])=2
⇒LCP(Ss[1],Ss[4])=1
⇒LCP(Ss[1],Ss[2])≥1
s=abbab
50
3ab0
0abbab0
4b0
2bab0
1bbab0
l[0]=?
l[1]=2
l[2]=?
l[3]=?
l[4]≥1
LCP(Ss[0],Ss[3])=2
⇒LCP(Ss[1],Ss[4])=1
⇒LCP(Ss[1],Ss[2])≥1
s=abbab
50
3ab0
0abbab0
4b0
2bab0
1bbab0
l[0]=?
l[1]=2
l[2]=?
l[3]=?
l[4]≥1
s=abbab
50
3ab0
0abbab0
4b0
2bab0
1bbab0
l[0]=?
l[1]=2
l[2]=?
l[3]=?
l[4]=1
s=abbab
50
3ab0
0abbab0
4b0
2bab0
1bbab0
l[0]=?
l[1]=2
l[2]=?
l[3]=?
l[4]=1
s=abbab
50
3ab0
0abbab0
4b0
2bab0
1bbab0
l[0]=?
l[1]=2
l[2]=?
l[3]=?
l[4]=1
LCP(Ss[1],Ss[2])=1
⇒LCP(Ss[2],Ss[3])=0
⇒LCP(Ss[2],Ss[4])≥0
s=abbab
50
3ab0
0abbab0
4b0
2bab0
1bbab0
l[0]=?
l[1]=2
l[2]=?
l[3]≥0
l[4]=1
LCP(Ss[1],Ss[2])=1
⇒LCP(Ss[2],Ss[3])=0
⇒LCP(Ss[2],Ss[4])≥0
s=abbab
50
3ab0
0abbab0
4b0
2bab0
1bbab0
l[0]=?
l[1]=2
l[2]=?
l[3]≥0
l[4]=1
s=abbab
50
3ab0
0abbab0
4b0
2bab0
1bbab0
l[0]=?
l[1]=2
l[2]=?
l[3]≥0
l[4]=1
s=abbab
50
3ab0
0abbab0
4b0
2bab0
1bbab0
l[0]=?
l[1]=2
l[2]=?
l[3]=1
l[4]=1
s=abbab
50
3ab0
0abbab0
4b0
2bab0
1bbab0
l[0]=?
l[1]=2
l[2]=?
l[3]=1
l[4]=1
LCP(Ss[2],Ss[4])=1
⇒LCP(Ss[3],Ss[5])=0
⇒LCP(Ss[3],Ss[5])≥0
s=abbab
50
3ab0
0abbab0
4b0
2bab0
1bbab0
l[0]≥0
l[1]=2
l[2]=?
l[3]=1
l[4]=1
LCP(Ss[2],Ss[4])=1
⇒LCP(Ss[3],Ss[5])=0
⇒LCP(Ss[3],Ss[5])≥0
s=abbab
50
3ab0
0abbab0
4b0
2bab0
1bbab0
l[0]≥0
l[1]=2
l[2]=?
l[3]=1
l[4]=1
s=abbab
50
3ab0
0abbab0
4b0
2bab0
1bbab0
l[0]=0
l[1]=2
l[2]=?
l[3]=1
l[4]=1
s=abbab
50
3ab0
0abbab0
4b0
2bab0
1bbab0
l[0]=0
l[1]=2
l[2]=?
l[3]=1
l[4]=1
s=abbab
50
3ab0
0abbab0
4b0
2bab0
1bbab0
l[0]=0
l[1]=2
l[2]=?
l[3]=1
l[4]=1
s=abbab
50
3ab0
0abbab0
4b0
2bab0
1bbab0
l[0]=0
l[1]=2
l[2]=0
l[3]=1
l[4]=1
s=abbab
50
3ab0
0abbab0
4b0
2bab0
1bbab0
l[0]=0
l[1]=2
l[2]=0
l[3]=1
l[4]=1
其實是懶得寫證明
vector<int> LCP(string s, vector<int> &r) {
int n = s.size();
vector<int> p(n + 1), l(n);
for (int i = 0; i <= n; i++) p[r[i]] = i;
int len = 0;
for (int i = 0; i < n; i++) {
int j = p[r[i] - 1];
while (i + len < n && j + len < n && s[i + len] == s[j + len]) len++;
l[r[i] - 1] = len;
if (len) len--;
}
return l;
}
各種毒瘤延伸: