[ICPC2016上海F] Mr. Panda and Fantastic Beasts - SAM
Description
给定 N 个串,求第一个串的子串中不在后面几个串中出现的最短子串,最小化字典序。
Solution
对第一个串建立 SAM,把后面的所有串扔上去跑,打标记(封禁某个结点表示的所有长于某个值的子串)
这样很容易求出答案的长度,问题是怎么构造字典序最小
最后用了个很暴力的方法,对于每个结点,如果没有被封禁并且包含等于目标长度的串,我们就拿去刷一下答案
#include <bits/stdc++.h>
using namespace std;
const int N = 1000005;
#define reset(x) memset(x, 0, sizeof x)
int caseid = 0;
struct SAM
{
int len[N];
int ch[N][26];
int fa[N];
int ind;
int last;
int t[N];
int a[N];
int cnt[N];
int f[N];
int tag[N];
int vis[N];
int from[N];
int minf;
string ans;
string str;
void init()
{
reset(len);
reset(ch);
reset(fa);
reset(t);
reset(a);
reset(cnt);
memset(f, 0, sizeof f);
reset(tag);
reset(vis);
reset(from);
ind = last = 1;
minf = 1e9;
ans = "";
}
void extend(int id)
{
int cur = ++ind;
int p;
len[cur] = len[last] + 1;
cnt[cur] = 1;
for (p = last; p && !ch[p][id]; p = fa[p])
ch[p][id] = cur;
if (!p)
fa[cur] = 1;
else
{
int q = ch[p][id];
if (len[q] == len[p] + 1)
fa[cur] = q;
else
{
int tmp = ++ind;
len[tmp] = len[p] + 1;
for (int i = 0; i < 26; i++)
ch[tmp][i] = ch[q][i];
fa[tmp] = fa[q];
for (; p && ch[p][id] == q; p = fa[p])
ch[p][id] = tmp;
fa[cur] = fa[q] = tmp;
}
}
last = cur;
}
void transtag()
{
memset(t, 0, sizeof t);
for (int i = 1; i <= ind; i++)
t[len[i]]++;
for (int i = 1; i <= ind; i++)
t[i] += t[i - 1];
for (int i = 1; i <= ind; i++)
a[t[len[i]]--] = i;
for (int i = ind; i >= 1; i--)
f[fa[a[i]]] = max(f[fa[a[i]]], f[a[i]]);
for (int p = 1; p <= ind; p++)
if (f[p] >= len[p])
f[p] = 1e9;
else
f[p] = max(len[fa[p]] + 1, f[p] + 1);
minf = 1e9;
for (int i = 1; i <= ind; i++)
if (f[i] < minf)
minf = f[i];
for (int i = 1; i <= ind; i++)
if (f[i] <= minf)
f[i] = 0;
else
f[i] = 1;
for (int i = 1; i <= ind; i++)
tag[fa[a[i]]] = max(tag[fa[a[i]]], tag[a[i]]);
}
void go(const string &s)
{
int n = s.length();
int p = 1;
int tmp = 0;
for (int i = 0; i < n; i++)
{
while (p > 1 && ch[p][s[i] - 'a'] == 0)
p = fa[p], tmp = len[p];
if (ch[p][s[i] - 'a'])
++tmp, p = ch[p][s[i] - 'a'];
f[p] = max(f[p], tmp);
}
}
void maketag(const string &s)
{
int n = s.length();
int p = 1;
for (int i = 0; i < n; i++)
{
p = ch[p][s[i] - 'a'];
tag[p] = i + 1;
}
}
void printans()
{
for (int i = 1; i <= ind; i++)
{
if (len[fa[i]] < minf && minf <= len[i])
{
int r = tag[i];
int l = r - minf + 1;
if (f[i])
continue;
string tmp = str.substr(l - 1, r - l + 1);
if (ans == "" || ans > tmp)
ans = tmp;
}
}
if (ans != "")
cout << ans << endl;
else
cout << "Impossible" << endl;
}
} sam;
void solve()
{
++caseid;
int n;
cin >> n;
sam.init();
string s;
cin >> s;
string s0 = s;
sam.str = s;
for (int i = 0; i < s.size(); i++)
{
sam.extend(s[i] - 'a');
}
for (int i = 1; i < n; i++)
{
cin >> s;
sam.go(s);
}
sam.maketag(s0);
sam.transtag();
cout << "Case #" << caseid << ": ";
sam.printans();
}
signed main()
{
ios::sync_with_stdio(false);
int t;
cin >> t;
while (t--)
solve();
}