搬走了:哈希yyds学习&总结(全)
//首先得说明,模板都是"xyz"=x*B^2+y*B+z
//结构体方便双哈希
//哈希值不同子串一定不同,哈希值相同子串不一定相同。双哈希最保险
//#define int long long感觉是必须的,因为B=23,M=1e9+7,,,
struct HASH {
//不过好在HASH还是很快
int B; // B进制
int M; //大素数取余
int b[N]; // b[i]=b^i%M
int a[N]; //哈希前缀
void init(int n, int x, int y) {
b[0] = 1, B = x, M = y;
for (int i = 1; i <= n; i++) b[i] = b[i - 1] * B % M;
}
//核心操作:求子串哈希值
int get(int l, int r) {
return (a[r] - a[l - 1] * b[r - l + 1] % M + M) % M;
}
//#################以上是基础操作 ,以下随题目:::
} h[2];
E. Compress Words
题解:hash,其他比如kmp算法应该也可以,但是hsah yyds!!!
代码:
#include
// #define int unsigned long long
#define int long long
#define dbg(x) cout << #x << "===" << x << endl
using namespace std;
const int N = 1e6 + 10;
int n;
char c[N], s[N];
int cnt = 0;
//方便双哈希?
struct HASH {
//#define int long long感觉是必须的,因为B=23,M=1e9+7,,,
//不过好在HASH还是很快
int B; // B进制
int M; //大素数取余
int b[N]; // b[i]=b^i%M
int a[N]; //哈希前缀
void init(int n, int x, int y) {
b[0] = 1, B = x, M = y;
for (int i = 1; i <= n; i++) b[i] = b[i - 1] * B % M;
}
int get(int l, int r) {
return (a[r] - a[l - 1] * b[r - l + 1] % M + M) % M;
}
//#################以上是基础操作 ,以下随题目:::
} h[2];
int d[2][N];
void add() {
int len = strlen(c + 1), mx = 0;
for (int i = 1; i <= len; i++) {
bool f = true;
//双哈希--------循环(代码更容易写)
for (int j = 0; j < 2; j++) {
d[j][i] = (d[j][i - 1] * h[j].B + c[i]) % h[j].M;
if (cnt < i)
f = false;
else if (h[j].get(cnt - i + 1, cnt) != d[j][i])
f = false;
}
if (f) mx = i;
}
// dbg(mx);
for (int i = mx + 1; i <= len; i++) {
++cnt;
for (int j = 0; j < 2; j++) {
h[j].a[cnt] = (h[j].a[cnt - 1] * h[j].B + c[i]) % h[j].M;
}
s[cnt] = c[i];
}
}
signed main() {
h[0].init(N - 1, 233, 1e9 + 7);
h[1].init(N - 1, 2333, 1e9 + 9);
cin >> n;
while (n--) {
cin >> c + 1;
add();
}
cout << s + 1 << endl;
return 0;
}
#include
#define int long long
#define dbg(x) cout << #x << "===" << x << endl
using namespace std;
const int N = 1500 + 10;
int n, m;
char c[N];
//方便双哈希?
struct HASH {
//#define int long long感觉是必须的,因为B=23,M=1e9+7,,,
//不过好在HASH还是很快
int B; // B进制
int M; //大素数取余
int b[N]; // b[i]=b^i%M
int a[N]; //哈希前缀
void init(int n, int x, int y) {
b[0] = 1, B = x, M = y;
for (int i = 1; i <= n; i++) b[i] = b[i - 1] * B % M;
}
int get(int l, int r) {
return (a[r] - a[l - 1] * b[r - l + 1] % M + M) % M;
}
//#################以上是基础操作 ,以下随题目:::
int add() {
m = strlen(c + 1);
for (int i = 1; i <= m; i++) a[i] = (a[i - 1] * B + c[i]) % M;
return a[m];
}
} h[2];
signed main() {
h[0].init(N - 1, 233, 1e9 + 7);
h[1].init(N - 1, 2333, 1e9 + 9);
cin >> n;
set<int> st[2];
while (n--) {
// cin >> m;
cin >> c + 1;
for (int j = 0; j < 2; j++) {
st[j].insert(h[j].add());
}
}
int ans = 0;
for (int j = 0; j < 2; j++) {
int t = st[j].size();
ans = max(ans, t);
}
cout << ans << endl;
return 0;
}
//在线算法:即,其实可以一个字符一个字符输入
struct KMP {
//与其说是KMP算法,不如说是前缀函数算法。而KMP也只是它的一个应用
int pi[N]; //前缀数组
//得到前缀函数,即前缀数组的值
//注意进入函数的是s还是s+1?
//当然是s,我发现,我之前就,从来没写过s+1的kmp。(之后多刷题,再说吧)
void get(char *s, int l) {
for (int i = 1; i < l; i++) {
int j = pi[i - 1];
while (j > 0 && s[i] != s[j]) j = pi[j - 1];
if (s[i] == s[j]) j++;
pi[i] = j;
}
}
//######################根据题目不同写不同的东西咯:
} kmp;
i-(n-1)-n=i-2*n+1
#include
#define dbg(x) cout << #x << "===" << x << endl
using namespace std;
const int N = 2e6 + 10;
char s[N], t[N];
int n, m;
//在线算法:即,其实可以一个字符一个字符输入
struct KMP {
//与其说是KMP算法,不如说是前缀函数算法。而KMP也只是它的一个应用
int pi[N]; //前缀数组
//注意进入函数的是s还是s+1?
//当然是s,我发现,我之前就,从来没写过s+1的kmp。(之后多刷题,再说吧)
void get(char *s, int l) {
for (int i = 1; i < l; i++) {
int j = pi[i - 1];
while (j > 0 && s[i] != s[j]) j = pi[j - 1];
if (s[i] == s[j]) j++;
pi[i] = j;
}
}
//######################根据题目不同写不同的东西咯:
void solve(int n, int len) {
for (int i = n + 1; i < len; i++) {
if (pi[i] == n) printf("%d\n", i - 2 * n + 1);
//从1开始:为什么是i-2*n,这里还是要小推一下i-(n-1)-(n+1),i-(n-1)是s+'#'+t中起点,再-(n+1)才是t中起点
//从0开始:i-(n-1)-n=i-2*n+1
}
for (int i = 0; i < n; i++) printf("%d ", pi[i]);
}
} kmp;
signed main() {
cin >> t >> s; //注意先输入哪一个串,在t中查找s
n = strlen(s);
m = strlen(t);
//以下也算是kmp.init()部分吧。不过不是求pi函数的关键
int len = n;
s[len++] = '#'; //'#'为不可能出现的字符
for (int i = 0; i < m; i++) s[len++] = t[i];
// dbg(s + 1);
kmp.get(s, len); //注意是s还是s+1
kmp.solve(n, len);
return 0;
}
struct EKMP {
//首先约定:字符串下标从0开始
//可能是有必要,在求z之前,对z进行初始化为0 的,特别是z[0]=n的时候。
int z[N]; // z[i]定义:s和s[i,n-1]的最长公共前缀的长度。
//叫做z函数,特别的z[0]=0(也不一定,有时候题目就会暗示z[0]=n,注意变通就ok了)
void z_function(char *s, int n) {
int l = 0, r = 0; //算法的过程中我们维护右端点最靠右的匹配段[l,r]
for (int i = 1; i < n; i++) {
//如果i<=r,[i,r]和[i-l,r-l]与s的最长公共前缀应该相等
if (i <= r && z[i - l] < r - i + 1) {
z[i] = z[i - l];
} else {
z[i] = max(0, r - i + 1);
while (i + z[i] < n && s[z[i]] == s[i + z[i]]) ++z[i];
}
if (i + z[i] - 1 > r) l = i, r = i + z[i] - 1;
//注意每次需要O(1)更新一下下
}
// z[0]=n;//有时候题目会有这个要求,但是有时候就z[0]=0
}
//这里也是,根据题目要求不同有不同操作:::
} ekmp;
i-2*n+1
。t=c+s
(注意是反串),然后可以增加|t|-Zmax
个新子串。即 O ( n ) O(n) O(n)可以求解:增加一个字符,增加了多少子串。总结:
i+z[i]=n
的i
。struct Trie {
// N大于可能出现的字符的总数
int ch[N][30], cnt;
int tag[N]; //给结尾打标记,表示这个字符串的数量
//插入字符串,注意这里下标从0开始
void insert(char *s, int l) {
int p = 0;
for (int i = 0; i < l; i++) {
int c = s[i] - 'a';
if (!ch[p][c]) ch[p][c] = ++cnt; //如果没有,就添加结点
p = ch[p][c];
}
tag[p]++; //一般赋值为1就ok了
}
//查找字符串
int query(char *s, int l) {
int p = 0;
for (int i = 0; i < l; i++) {
int c = s[i] - 'a';
if (!ch[p][c]) return 0;
p = ch[p][c];
}
return tag[p]; //返回个数,一般来说为1
}
//##################其他操作(依题而变):::::::::::
} trie;
#include
// #define int long long
#define dbg(x) cout << #x << "===" << x << endl
using namespace std;
const int N = 1e4 * 50 + 10; // 5e4
int n, m;
char s[60];
struct Trie {
// N大于可能出现的字符的总数
int ch[N][30], cnt;
int tag[N]; //给结尾打标记,表示这个字符串的数量
//插入字符串,注意这里下标从0开始
void insert(char *s, int l) {
int p = 0;
for (int i = 0; i < l; i++) {
int c = s[i] - 'a';
if (!ch[p][c]) ch[p][c] = ++cnt; //如果没有,就添加结点
p = ch[p][c];
}
tag[p]++; //一般赋值为1就ok了
}
//查找字符串
int query(char *s, int l) {
int p = 0;
for (int i = 0; i < l; i++) {
int c = s[i] - 'a';
if (!ch[p][c]) return 0;
p = ch[p][c];
}
return tag[p]; //返回个数,一般来说为1
}
//##################其他操作(依题而变):::::::::::
int ask(char *s, int l) {
int p = 0;
for (int i = 0; i < l; i++) {
int c = s[i] - 'a';
if (!ch[p][c]) return -1; //表示不存在
p = ch[p][c];
}
return p; //返回尾结点,然后我们标记
}
} trie;
signed main() {
cin >> n;
while (n--) {
cin >> s;
trie.insert(s, strlen(s));
}
int mp[N];
cin >> m;
while (m--) {
cin >> s;
int t = trie.ask(s, strlen(s));
if (t == -1)
puts("WRONG");
else {
if (!mp[t])
mp[t] = 1, puts("OK");
else
puts("REPEAT");
}
}
return 0;
}