XEN 3166
这题原题是spj,校oj上只用判断yes no,不过也差不多
题意分析之后就是求两个东西:
- 字典序最小的长度为m的子序列
- 同时这个字典序严格大于某个字符串
用序列自动机 先尽量相同,然后再考虑严格大于
#include <bits/stdc++.h>
using namespace std;
const int maxn = 200010;
int last[27], nxt[maxn][27], len;
void init(const char s[]) {
len = (int) strlen(s);
for (int i = 0; i < 26; ++i) last[i] = len + 1;
for (int i = len; i >= 1; i--) {
for (int j = 0; j < 26; ++j) {
nxt[i][j] = last[j];
}
last[s[i - 1] - 'A'] = i;
}
for (int j = 0; j < 26; ++j) {
nxt[0][j] = last[j];
}
}
char s[2][maxn];
int n, m;
int solve(char pre[], char ans[], const char name[]) {
if (len < m) return false;
if (pre[1] == name[0]) {
if (m <= 1) return 0;
int a = 0, b = 0, c = 0;
for (int i = 1, j = 1; i < m && j <= len; ++i) {
ans[i] = pre[i];
for (int k = pre[i + 1] - 'A' + 1; k < 26; ++k) {
if (i + len - nxt[j][k] + 1 >= m) {
a = i;
b = j;
c = k;
break;
}
}
j = nxt[j][pre[i + 1] - 'A'];
}
if (!a) return 0;
ans[a + 1] = char(c + 'A');
//
for (int i = a + 1, j = nxt[b][c]; i < m; ++i) {
for (int k = 0; k < 26; ++k) {
if (i + len - nxt[j][k] + 1 >= m) {
ans[i + 1] = char(k + 'A');
j = nxt[j][k];
break;
}
}
}
} else {
ans[1] = name[0];
for (int i = 1, j = nxt[0][name[0] - 'A']; i < m; ++i) {
for (int k = 0; k < 26; ++k) {
if (i + len - nxt[j][k] + 1 >= m) {
ans[i + 1] = char(k + 'A');
j = nxt[j][k];
break;
}
}
}
}
return 1;
}
string p[1010];
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
cin >> p[i];
}
sort(p + 1, p + 1 + n);
bool flag = 1;
for (int i = 1; i <= n; ++i) {
init(p[i].c_str());
if (!solve(s[i % 2], s[1 - i % 2], p[i].c_str())) {
flag = 0;
break;
}
}
if (flag) cout << "YES" << '\n';
else cout << "NO" << '\n';
return 0;
}