UVA - 11107 Life Forms
按字典序求出所有至少在一半字符串内出现的公共子串。
朴素想法是多串以未出现字符分割,然后前推right集合,用位运算来判断当前节点被几个串更新过(这里n最大有100,ull也存不下,所以用bitset)。
但是会t。猜测原因可能是自动机太大了,还要跑基数排序。
用广义sam就过了。
至于输出具体的子串,按字典序dfs即可。
ac代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 300005;
char s[maxn];
struct Sam {
int next[maxn][27];
int link[maxn], step[maxn];
bitset<105> mark[maxn];
int a[maxn], b[maxn];
int sz, last, root, maxx;
char ans[maxn];
void init() {
memset(next, 0, sizeof(next));
memset(a, 0, sizeof(a));
for(int i = 0; i <= sz; i++) {
mark[i].reset();
}
sz = last = root = 1;
maxx = -1;
}
void add(int c) {
if(next[last][c] && step[last] + 1 == step[next[last][c]]) {
last = next[last][c];
return;
}
int p = last;
int np = ++sz;
last = np;
step[np] = step[p] + 1;
while(!next[p][c] && p) {
next[p][c] = np;
p = link[p];
}
if(p == 0) {
link[np] = root;
} else {
int q = next[p][c];
if(step[p] + 1 == step[q]) {
link[np] = q;
} else {
int clone = ++sz;
memcpy(next[clone], next[q], sizeof(next[q]));
step[clone] = step[p] + 1;
link[clone] = link[q];
link[q] = link[np] = clone;
while(next[p][c] == q && p) {
next[p][c] = clone;
p = link[p];
}
}
}
}
void dfs(int x, int len, int n) {
if(len > maxx || step[x] != len) {
return;
}
if(step[x] == maxx && mark[x].count() > n) {
ans[len] = '\0';
printf("%s\n", ans);
return;
}
for(int i = 0; i < 26; i++) {
if(next[x][i]) {
ans[len] = i + 'a';
dfs(next[x][i], len + 1, n);
ans[len] = 0;
}
}
}
void solve(int n) {
init();
int len, kind = -1;
for(int i = 0; i < n; i++) {
scanf("%s", s);
len = strlen(s);
last = 1;
for(int j = 0; j < len; j++) {
add(s[j] - 'a');
mark[last][i] = 1;
}
add(26);
kind = max(kind, len);
}
n >>= 1;
for(int i = 1; i <= sz; i++) {
a[step[i]]++;
}
for(int i = 1; i <= kind; i++) {
a[i] += a[i - 1];
}
for(int i = 1; i <= sz; i++) {
b[a[step[i]]--] = i;
}
for(int i = sz; i > 1; i--) {
int e = b[i];
mark[link[e]] |= mark[e];
if(mark[e].count() > n) {
// printf("%d\n",mark[e].count());
maxx = max(maxx, step[e]);
}
}
if(maxx == -1) {
printf("?\n");
} else {
dfs(root, 0, n);
}
}
} sam;
int main() {
int n;
scanf("%d", &n);
while(n) {
sam.solve(n);
scanf("%d", &n);
if(n){
printf("\n");
}
}
return 0;
}
UVA - 12206 Stammering Aliens
求最长重复子串,同时输出他最后一次出现的起始下标。
维护一个start数组。
说是start,其实是end,是每个后缀结束的位置,再通过start[e] - step[e] + 1求出start。
注意考虑最大长度同时也要选取其中最靠右的。
比上一题简单。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 40005;
char s[maxn];
struct Sam {
int next[maxn << 1][26];
int link[maxn << 1], step[maxn << 1];
int endpos[maxn << 1];
int start[maxn << 1];
int a[maxn], b[maxn << 1];
int sz, last, len;
void init() {
//如多次建立自动机,加入memset操作
memset(next, 0, sizeof(next));
memset(a, 0, sizeof(a));
memset(endpos, 0, sizeof(endpos));
memset(start, 0, sizeof(start));
sz = last = 1;
}
void add(int c, int m) {
int p = last;
int np = ++sz;
last = np;
endpos[np] = 1;
start[np] = m;
step[np] = step[p] + 1;
while(!next[p][c] && p) {
next[p][c] = np;
p = link[p];
}
if(p == 0) {
link[np] = 1;
} else {
int q = next[p][c];
if(step[p] + 1 == step[q]) {
link[np] = q;
} else {
int clone = ++sz;
memcpy(next[clone], next[q], sizeof(next[q]));
step[clone] = step[p] + 1;
link[clone] = link[q];
link[q] = link[np] = clone;
while(next[p][c] == q && p) {
next[p][c] = clone;
p = link[p];
}
}
}
}
void build() {
init();
len = strlen(s);
for(int i = 0; i < len; i++) {
add(s[i] - 'a', i);
}
for(int i = 1; i <= sz; i++) {
a[step[i]]++;
}
for(int i = 1; i <= len; i++) {
a[i] += a[i - 1];
}
for(int i = 1; i <= sz; i++) {
b[a[step[i]]--] = i;
}
}
void solve(int m) {
build();
int ans = -1, ans2 = -1;
for(int i = sz; i > 1; i--) {
int e = b[i];
if(endpos[e] >= m) {
// printf("%d %d %d\n", endpos[e], step[e], start[e] - step[e] + 1);
if(step[e] >= ans) {
ans = step[e];
ans2 = max(ans2, start[e] - step[e] + 1);
}
}
endpos[link[e]] += endpos[e];
start[link[e]] = max(start[link[e]], start[e]);
}
if(~ans) {
printf("%d %d\n", ans, ans2);
} else {
printf("none\n");
}
}
} sam;
/*
3
baaaababababbababbab
*/
int main() {
int m;
while(~scanf("%d", &m) && m) {
scanf("%s", s);
sam.solve(m);
}
return 0;
}
开了一套后缀数组的题,结果题题都会做,感觉都是sam的题,想了一下这样性价比太低了,跳坑去学树论。