题意: 求第一个字符串在其他字符串中未出现过的最短, 且字典序最小的子串.
分析: 先将所有子串用特殊字符串连接起来, 用后缀数组算出子串的sa数组和lcp高度数组, 因为sa数组是按后缀从小到大排得, 然后从后往前遍历, 遇到第i个位置, 若sa[i] < s1.length()且与sa[j] > s1.length()位置的最长前缀比当前记录的前缀更小, 然后判断这个最长前缀是不是s1字符串的后缀, 如果不是, 则更新最小值. 例如: 已知s1 = “dddffa”, 和字符串s2 = “dfa”, 在得到s1中的’a’和s2中的’a’是最长前缀之后, 发现’a’已经是s1的后缀, 所以s1中以’a’开头的子串一定在s2中出现, 此时不更新答案.
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
#include<cmath>
#include<string>
typedef long long ll;
const int maxn = 3 * 1e5 + 10;
const ll mod = 1e9 + 7;
using namespace std;
int v = 1;
ll b[maxn], gg[maxn];
int T, n, k, r;
int rankx[maxn], sa[maxn], tmp[maxn], lcp[maxn];
string s, t;
bool cmp(int i, int j) {
if(rankx[i] != rankx[j]) return rankx[i] < rankx[j];
int ri = i + k <= n ? rankx[i + k] : -1;
int rj = j + k <= n ? rankx[j + k] : -1;
return ri < rj;
}
void sa_(string st) {
n = st.length();
for(int i = 0; i <= n; i++) {
sa[i] = i;
rankx[i] = i < n ? st[i] : -1;
}
for(k = 1; k <= n; k *= 2) {
sort(sa, sa + n + 1, cmp);
tmp[sa[0]] = 0;
for(int i = 1; i <= n; i++) {
tmp[sa[i]] = tmp[sa[i - 1]] + (cmp(sa[i - 1], sa[i]) ? 1 : 0);
}
for(int i = 0; i <= n; i++) {
rankx[i] = tmp[i];
}
}
}
void lcp_(string st) {
int n = st.length();
for(int i = 0; i <= n; i++) rankx[sa[i]] = i;
int h = 0;
lcp[0] = 0;
for(int i = 0; i < n; i++ ) {
int j = sa[rankx[i] - 1];
if(h > 0) h--;
for(; j + h < n && i + h < n; h++) {
if(st[j + h] != st[i + h]) break;
}
lcp[rankx[i] - 1] = h;
}
}
void solve() {
int ans = r - 1, gg = -1;
for(int i = n; i >= 1; i--) {
while(sa[i] >= r) i--;
if(i < 1) break;
int res = lcp[i - 1], rs = lcp[i];
for(int p = i - 1; p && sa[p] < r; p--) {
res = min(res, lcp[p]);
}
for(int p = i + 1; p <= n && sa[p] < r; p++) {
rs = min(rs, lcp[p]);
}
res = max(res, rs);
if(res < r - sa[i]) {
if(res <= ans) {
ans = res;
gg = sa[i];
}
}
}
printf("Case #%d: ", v++);
if(gg == -1) printf("Impossible\n");
else {
for(int i = gg; i <= gg + ans; i++) {
printf("%c", s[i]);
}
printf("\n");
}
}
int main() {
scanf("%d", &T);
while(T--) {
scanf("%d", &n);
s = "";
cin >> s;
r = s.length();
char c = 'z' + 1;
for(int i = 1; i < n; i++) {
cin >> t;
s += c + t;
}
int l = s.length();
sa_(s);
lcp_(s);
// for(int i = 1; i <= l; i++) {
// printf("%d %d\n", sa[i], lcp[i]);
// }
solve();
}
return 0;
}