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;
}
posted @ 2017-10-03 18:54  ftae  阅读(153)  评论(0编辑  收藏  举报