hdu5558
hdu5558
题意
给出一个字符串,按照特殊规则进行加密。
假设已经加密了前 \(i\) 个字符,从第 \(i+1\) 个字符开始找到 \(S[i..N]\) 的长度为 \(K\) 的最长前缀等于 \(S[T...T+K-1]\) ,其中 \(T < i\)。要求 \(K\) 尽可能大, \(T\) 尽可能小。如果找不到输出 \(-1\) 以及当前字符的 \(ASCII\) 值,否则输出 \(K\ T\) ,然后 \(i = i + K\) 继续加密。
分析
按照题意去模拟,关键在于怎么快速找出前面出现过的子串。
我们每次可能要插入一个或多个字符,后缀自动机可以很方便的找出前面出现过的子串(直接在后缀自动机上跑就行了)。
后缀自动机能识别所有已经插入的字符串的子串。
题目要求 \(T\) 最小,在插入所有字符后,我们可以预处理出每个状态下子串的最小的右端点下标。
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 10;
int ch[MAXN << 1][26];
int fa[MAXN << 1]; // 前继结点,如果当前结点可以接受某个后缀那么它的前继结点也可以接受
int len[MAXN << 1]; // 从根结点到该结点的最大距离,这个状态(结点)代表的串长度区间 (len[fa], len]
int cnt, last;
void init() {
memset(ch, 0, sizeof ch);
memset(fa, 0, sizeof fa);
last = cnt = 1; // root节点为 1 ,所以添加的字符从 2 开始
}
vector<int> nd;
void clear() {
for(int i = 0; i <= cnt; i++) {
memset(ch[i], 0, sizeof ch[i]);
fa[i] = len[i] = 0;
}
last = cnt = 1;
}
void add(int c) {
int p = last, np = last = ++cnt;
nd.push_back(np);
len[np] = len[p] + 1;
while(!ch[p][c] && p) {
ch[p][c] = np;
p = fa[p];
}
if(p == 0) fa[np] = 1;
else {
int q = ch[p][c];
if(len[p] + 1 == len[q]) {
fa[np] = q;
} else {
int nq = ++cnt;
len[nq] = len[p] + 1;
memcpy(ch[nq], ch[q], sizeof ch[q]);
fa[nq] = fa[q];
fa[q] = fa[np] = nq;
while(ch[p][c] == q && p) {
ch[p][c] = nq;
p = fa[p];
}
}
}
}
char S[MAXN];
int al[MAXN], ar[MAXN];
int st[MAXN << 1];
int main() {
int T, kase = 1;
last = 1; cnt = 1;
scanf("%d", &T);
while(T--) {
nd.clear();
scanf("%s", S);
int Len = strlen(S);
int ct = 0;
add(S[0] - 'a');
al[ct] = -1; ar[ct++] = S[0];
for(int i = 1; i < Len; ) {
int c = S[i] - 'a';
int now = 1, l = 0;
while(ch[now][c] && i + l < Len) {
now = ch[now][c];
add(c);
l++;
c = S[i + l] - 'a';
}
if(l) {
al[ct] = l; ar[ct++] = now;
} else {
al[ct] = -1; ar[ct++] = S[i];
add(S[i] - 'a');
}
i += l;
if(!l) i++;
}
for(int i = 0; i < nd.size(); i++) {
int tmp = nd[i];
while(tmp != 1) {
if(!st[tmp]) {
st[tmp] = len[nd[i]];
} else break;
tmp = fa[tmp];
}
}
printf("Case #%d:\n", kase++);
for(int i = 0; i < ct; i++) {
if(al[i] == -1) printf("-1 %d\n", ar[i]);
else printf("%d %d\n", al[i], st[ar[i]] - al[i]);
}
for(int i = 0; i <= cnt; i++) st[i] = 0;
clear();
}
return 0;
}