文章目录
hdu2222 - Keywords Search
思路
AC自动机的模板题
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int N = 50 * 10000 + 10, M = 1e6 + 10;
int tr[N][26], ne[N], cnt[N], idx;
char s[M];
void insert(char str[]) {
int p = 0;
for(int i = 0; str[i]; i++) {
int id = str[i] - 'a';
if(!tr[p][id]) {
tr[p][id] = ++idx;
memset(tr[idx], 0, sizeof tr[idx]);
cnt[idx] = ne[idx] = 0;
}
p = tr[p][id];
}
cnt[p]++;
}
int q[N];
void build() {
int l = 1, r = 0;
for(int i = 0; i < 26; i++) {
if(tr[0][i]) q[++r] = tr[0][i];
}
while(l <= r) {
int u = q[l++];
for(int i = 0; i < 26; i++) {
int c = tr[u][i];
if(!c) tr[u][i] = tr[ne[u]][i];
else {
ne[c] = tr[ne[u]][i];
q[++r] = c;
}
}
}
}
void solve() {
int n;
scanf("%d", &n);
memset(tr[0], 0, sizeof tr[0]);
idx = 0, cnt[0] = ne[0] = 0;
for(int i = 1; i <= n; i++) {
scanf("%s", s);
insert(s);
}
build();
scanf("%s", s + 1);
int len = strlen(s + 1);
int res = 0;
for(int i = 1, j = 0; i <= len; i++) {
int id = s[i] - 'a';
j = tr[j][id];
int p = j;
while(p && ~cnt[p]) {
res += cnt[p];
cnt[p] = -1;
p = ne[p];
}
}
printf("%d\n", res);
}
int main() {
// freopen("in.txt", "r", stdin);
int t; cin >> t; while(t--)
solve();
return 0;
}
hdu2896 - 病毒侵袭
思路
还是AC自动机模板题,只是在小范围上处理比较复杂,对于每个串单独判断一遍,同时用vector记录一下跑到哪些病毒串即可。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int N = 1e5 + 10;
int tr[N][130], ne[N], idx, cnt[N];
bool vis[N];
char str[N];
void insert(char s[], int id) {
int p = 0;
for(int i = 0; s[i]; i++) {
int id = s[i];
if(!tr[p][id]) {
tr[p][id] = ++idx;
memset(tr[idx], 0, sizeof tr[idx]);
cnt[idx] = 0;
ne[idx] = 0;
}
p = tr[p][id];
}
cnt[p] = id;
}
int q[N];
void build() {
int l = 1, r = 0;
for(int i = 0; i <= 128; i++) {
if(tr[0][i]) q[++r] = tr[0][i];
}
while(l <= r) {
int u = q[l++];
for(int i = 0; i <= 128; i++) {
int c = tr[u][i];
if(!c) tr[u][i] = tr[ne[u]][i];
else {
ne[c] = tr[ne[u]][i];
q[++r] = c;
}
}
}
}
void solve() {
int n;
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%s", str);
insert(str, i);
}
build();
int m; scanf("%d", &m);
int t = 0;
for(int id = 1; id <= m; id++) {
scanf("%s", str + 1);
vector<int> res;
memset(vis, 0, sizeof vis);
for(int i = 1, j = 0; str[i]; i++) {
int id = str[i];
j = tr[j][id];
int p = j;
while(p && !vis[p]) {
if(cnt[p]) res.push_back(cnt[p]);
vis[p] = true;
p = ne[p];
}
}
if(res.size() > 0) {
t++;
sort(res.begin(), res.end());
printf("web %d:", id);
for(int i = 0; i < res.size(); i++) {
printf(" %d", res[i]);
}
puts("");
}
}
printf("total: %d\n", t);
}
int main() {
// freopen("in.txt", "r", stdin);
solve();
return 0;
}
hdu3065 - 病毒侵袭持续中
思路
模板题,多开一个cnt数组记录每个病毒串出现几次输出即可。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int N = 5e4 + 10, M = 2e6 + 10;
int tr[N][26], ne[N], cnt[N], idx;
char str[M];
char s[1010][55];
int res[1010], n;
void insert(char s[], int id) {
int p = 0;
for(int i = 0; s[i]; i++) {
int id = s[i] - 'A';
if(!tr[p][id]) {
tr[p][id] = ++idx;
memset(tr[idx], 0, sizeof tr[idx]);
cnt[idx] = ne[idx] = 0;
}
p = tr[p][id];
}
cnt[p] = id;
}
queue<int> q;
void build() {
for(int i = 0; i < 26; i++) {
if(tr[0][i]) q.push(tr[0][i]);
}
while(!q.empty()) {
int u = q.front(); q.pop();
for(int i = 0; i < 26; i++) {
int c = tr[u][i];
if(!c) tr[u][i] = tr[ne[u]][i];
else {
ne[c] = tr[ne[u]][i];
q.push(c);
}
}
}
}
void solve() {
// int n;
// scanf("%d", &n);
idx = 0;
memset(tr[0], 0, sizeof tr[0]);
ne[0] = cnt[0] = 0;
for(int i = 1; i <= n; i++) {
scanf("%s", s[i]);
insert(s[i], i);
res[i] = 0;
}
build();
scanf("%s", str);
for(int i = 0, j = 0; str[i]; i++) {
if(str[i] < 'A' || str[i] > 'Z') {
j = 0;
continue;
}
int id = str[i] - 'A';
j = tr[j][id];
int p = j;
while(p) {
if(cnt[p]) res[cnt[p]]++;
p = ne[p];
}
}
for(int i = 1; i <= n; i++) {
if(res[i]) {
printf("%s: %d\n", s[i], res[i]);
}
}
}
int main() {
// freopen("in.txt", "r", stdin);
while(~scanf("%d", &n)) {
solve();
}
// solve();
return 0;
}
poj2778-DNA Sequence
思路
一开始想着肯定是一个经典的ac自动机+dp题,但是数据范围很大,就需要考虑别的办法。
通过ac自动机建立一个trie图,用vis数组标记一下表示不能到达哪些点。然后通过建好的ac自动机建立矩阵快速幂,矩阵快速幂m[i][j]表示在trie图上从i点到j的方案数。离散数学中可以知道在一个图上走了n次,就相当于是所建矩阵的n次幂。拿矩阵快速幂跑n次以后,计算m[0][i]表示从起点0到tire图上i点的所有方案数,累加一下即可。
代码
#include<iostream>
#include<map>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
typedef long long LL;
#define int LL
typedef pair<int, int> PII;
#define Martix Matrix
#define gcd __gcd
#define maxn 110
const int mod = 100000;
const int N = 110 + 10;
int tr[N][5], idx, ne[N], n, k;
char s[N];
bool vis[N];
map<char, int> mp;
struct Matrix {
int m[N][N];
Matrix() {
memset(m, 0, sizeof(m)); // 初始化矩阵
}
};
Matrix Multi(Matrix a, Matrix b) // 矩阵乘法
{
Matrix res;
for(int i = 0; i <= idx; i++) {
for(int j = 0; j <= idx; j++) {
for(int k = 0; k <= idx; k++) {
res.m[i][j] = (res.m[i][j] + a.m[i][k] * b.m[k][j]) % mod;
}
}
}
return res;
}
Martix fastm(Martix a, int n) // 矩阵快速幂
{
Martix res;
for(int i = 0; i <= idx; i++) {
// 等同于快速幂的res = 1的操作
res.m[i][i] = 1;
}
while(n) {
if(n & 1) res = Multi(res, a);
a = Multi(a, a);
n >>= 1;
}
return res;
}
void insert(char str[]) {
int p = 0;
for(int i = 0; str[i]; i++) {
int id = mp[str[i]];
if(!tr[p][id]) {
tr[p][id] = ++idx;
memset(tr[idx], 0, sizeof tr[idx]);
vis[idx] = false;
ne[idx] = 0;
}
p = tr[p][id];
}
vis[p] = true;
}
void build() {
queue<int> q;
for(int i = 0; i < 4; i++) {
if(tr[0][i]) q.push(tr[0][i]);
}
while(!q.empty()) {
int u = q.front(); q.pop();
for(int i = 0; i < 4; i++) {
int c = tr[u][i];
if(!c) tr[u][i] = tr[ne[u]][i];
else {
ne[c] = tr[ne[u]][i];
vis[c] |= vis[ne[c]];
q.push(c);
}
}
}
}
void solve() {
memset(tr[0], 0, sizeof tr[0]);
vis[0] = false, ne[0] = 0; idx = 0;
for(int i = 1; i <= n; i++) {
scanf("%s", s);
insert(s);
}
build();
Matrix x;
for(int i = 0; i <= idx; i++) {
if(vis[i]) continue;
for(int j = 0; j < 4; j++) {
if(!vis[tr[i][j]]) {
x.m[i][tr[i][j]]++;
}
}
}
x = fastm(x