HUID 5558 Alice's Classified Message 后缀数组+单调栈+二分

http://acm.hdu.edu.cn/showproblem.php?pid=5558

对于每个后缀suffix(i),想要在前面i - 1个suffix中找到一个pos,使得LCP最大。这样做O(n^2)

考虑到对于每一个suffix(i),最长的LCP肯定在和他排名相近的地方取得。

按排名大小顺序枚举位置,按位置维护一个递增的单调栈,对于每一个进栈的元素,要算一算栈内元素和他的LCP最大是多少。

如果不需要输出最小的下标,最大的直接是LCP(suffix(st[top]),  suffix(st[top - 1]))就是相邻的两个。

但是要求最小的下标,这样的话需要对整个栈进行一个遍历,找到下标最小的并且长度最大的。整个栈与新进来的栈顶元素的LCP存在单调性,单调递增,所以可以二分。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <string>
#include <stack>
#include <map>
#include <set>
#include <bitset>
#define X first
#define Y second
#define clr(u,v); memset(u,v,sizeof(u));
#define in() freopen("data.txt","r",stdin);
#define out() freopen("ans","w",stdout);
#define Clear(Q); while (!Q.empty()) Q.pop();
#define pb push_back
#define inf (0x3f3f3f3f)
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int INF = 0x3f3f3f3f;
const int maxn = 100000 + 2;
int sa[maxn], x[maxn], y[maxn], book[maxn];
bool cmp(int r[], int a, int b, int len) {
    return r[a] == r[b] && r[a + len] == r[b + len];
}
void da(char str[], int sa[], int lenstr, int mx) {
    int *fir = x, *sec = y, *ToChange;
    for (int i = 0; i <= mx; ++i) book[i] = 0;
    for (int i = 1; i <= lenstr; ++i) {
        fir[i] = str[i];
        book[str[i]]++;
    }
    for (int i = 1; i <= mx; ++i) book[i] += book[i - 1];
    for (int i = lenstr; i >= 1; --i) sa[book[fir[i]]--] = i;
    for (int j = 1, p = 1; p <= lenstr; j <<= 1, mx = p) {
        p = 0;
        for (int i = lenstr - j + 1; i <= lenstr; ++i) sec[++p] = i;
        for (int i = 1; i <= lenstr; ++i) {
            if (sa[i] > j)
                sec[++p] = sa[i] - j;
        }
        for (int i = 0; i <= mx; ++i) book[i] = 0;
        for (int i = 1; i <= lenstr; ++i) book[fir[sec[i]]]++;
        for (int i = 1; i <= mx; ++i) book[i] += book[i - 1];
        for (int i = lenstr; i >= 1; --i) sa[book[fir[sec[i]]]--] = sec[i];
//        ToChange = fir, fir = sec, sec = ToChange;
        swap(fir, sec);
        fir[sa[1]] = 0;
        p = 2;
        for (int i = 2; i <= lenstr; ++i) {
            fir[sa[i]] = cmp(sec, sa[i - 1], sa[i], j) ? p - 1 : p++;
        }
    }
}
int height[maxn], RANK[maxn];
void calcHight(char str[], int sa[], int lenstr) {
    for (int i = 1; i <= lenstr; ++i) RANK[sa[i]] = i;
    int k = 0;
    for (int i = 1; i <= lenstr - 1; ++i) {
        k -= k > 0;
        int j = sa[RANK[i] - 1];
        while (str[j + k] == str[i + k]) ++k;
        height[RANK[i]] = k;
    }
}
char str[maxn];
int dp[maxn][19];
vector<int> fuck[26];
const int need = 18;
void init_RMQ(int n, int a[]) {
    for (int i = 1; i <= n; ++i) {
        dp[i][0] = a[i];
    }
    for (int j = 1; j < need; ++j) {
        for (int i = 1; i + (1 << j) - 1 <= n; ++i) {
            dp[i][j] = min(dp[i][j - 1], dp[i + (1 << (j - 1))][j - 1]);
        }
    }
}
int ask(int be, int en) {
    if (be > en) swap(be, en);
    be++;
    int k = (int)log2(en - be + 1);
    return min(dp[be][k], dp[en - (1 << k) + 1][k]);
}
int f;
int st[maxn], top;
int t[maxn];
int ans[maxn], id[maxn];
bool check(int pos, int len) {
    int res = ask(RANK[st[pos]], RANK[st[top]]);
    return res == len;
}
void work() {
    memset(ans, 0, sizeof ans);
    memset(id, 0x3f, sizeof id);
    scanf("%s", str + 1);
    int lenstr = strlen(str + 1);
    str[lenstr + 1] = ‘$‘;
    str[lenstr + 2] = ‘\0‘;
    da(str, sa, lenstr + 1, 128);
    calcHight(str, sa, lenstr + 1);
//    for (int i = 1; i <= lenstr + 1; ++i) {
//        printf("%d ", sa[i]);
//    }
//    printf("\n");
    init_RMQ(lenstr + 1, height);
    printf("Case #%d:\n", ++f);
    top = 0;
    for (int i = 2; i <= lenstr + 1; ++i) {
        while (top >= 1 && sa[i] < st[top]) {
            top--;
        }
        st[++top] = sa[i];
        if (top >= 2) {
            int tlen = ask(RANK[st[top]], RANK[st[top - 1]]);
            ans[st[top]] = tlen;
            id[st[top]] = st[top - 1];
            int be = 1, en = top - 2;
            while (be <= en) {
                int mid = (be + en) >> 1;
                if (check(mid, tlen)) en = mid - 1;
                else be = mid + 1;
            }
            id[st[top]] = st[be];
        }
    }
//    for (int i = 1; i <= lenstr; ++i) {
//        printf("%d ", ans[i]);
//    }
//    printf("\n");

    top = 0;
    for (int i = lenstr + 1; i >= 2; --i) {
        while (top >= 1 && sa[i] < st[top]) {
            top--;
        }
        st[++top] = sa[i];
        if (top >= 2) {
            int tlen = ask(RANK[st[top]], RANK[st[top - 1]]);
            if (ans[st[top]] < tlen) {
                ans[st[top]] = tlen;
                id[st[top]] = st[top - 1];
            } else if (ans[st[top]] == tlen && id[st[top]] > st[top - 1]) {
                id[st[top]] = st[top - 1];
            } else if (ans[st[top]] > tlen) continue; // 太小了
            int be = 1, en = top - 2;
            while (be <= en) {
                int mid = (be + en) >> 1;
                if (check(mid, tlen)) en = mid - 1;
                else be = mid + 1;
            }
            if (check(be, tlen))
                id[st[top]] = min(id[st[top]], st[be]);
        }
    }
//    for (int i = 1; i <= lenstr; ++i) {
//        printf("%d ", ans[i]);
//    }
//    printf("\n");
    for (int i = 1; i <= lenstr;) {
        if (ans[i] == 0) {
            printf("%d %d\n", -1, str[i]);
            i++;
        } else {
            printf("%d %d\n", ans[i], id[i] - 1);
            i += ans[i];
        }
    }
}
int main()
{
#ifdef local
    in();
#else
#endif
//    printf("%d\n", 1 << 17);
    int t;
    scanf("%d", &t);
    while (t--) work();
    return 0;
}

HUID 5558 Alice's Classified Message 后缀数组+单调栈+二分

时间: 2024-12-13 06:05:45

HUID 5558 Alice's Classified Message 后缀数组+单调栈+二分的相关文章

(HDU 5558) 2015ACM/ICPC亚洲区合肥站---Alice&#39;s Classified Message(后缀数组)

题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=5558 Problem Description Alice wants to send a classified message to Bob. She tries to encrypt the message with her original encryption method. The message is a string S, which consists of Nlowercase let

【bzoj3238】[Ahoi2013]差异 后缀数组+单调栈

题目描述 输入 一行,一个字符串S 输出 一行,一个整数,表示所求值 样例输入 cacao 样例输出 54 题解 后缀数组+单调栈,几乎同 bzoj3879 的后半部分. 我明显是做题做反了... 这里还是说一下这道题的做法. 先用后缀数组求出height. 然后由于有LCP(a,c)=min(LCP(a,b),LCP(b,c))(rank[a]<rank[b]<rank[c]),所以我们只需要知道排名相邻的两个后缀的LCP,而这就是height数组的定义. 转化为子问题:给出n个数,求所有子

[HAOI2016]找相同字符(后缀数组+单调栈)

[HAOI2016]找相同字符(后缀数组+单调栈) 题面 给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数.两个方案不同当且仅当这两个子串中有一个位置不同. 分析 我们把两个字符串接在一起,中间加一个分隔符.如\(\text{AABB}\)和\(\text{BBAA}\)变成\(\text{AABB|BBAA}\).我们考虑两个相同字串,如\(\text{BB}\),它在新串中对应了两个后缀\(BB|BBAA\)和\(\text{BBAA}\)的LCP. 容易发现,LC

BZOJ 3238 AHOI 2013 差异 后缀数组+单调栈

题目大意: 思路:一看各种后缀那就是后缀数组没跑了. 求出sa,height之后就可以乱搞了.对于height数组中的一个值,height[i]来说,这个值能够作为lcp值的作用域只在左边第一个比他小的位置到右边第一个比他小的位置.这个东西很明显可以倍增RMQ+二分/单调栈. 之后就是数学题了 Σlen[Ti] + len[Tj] = (len + 1) * len * (len - 1),之后吧所有求出来的Σ2 * lcp(Ti,Tj)减掉就是答案. 记得答案开long long CODE:

POJ 3415 Common Substrings(后缀数组+单调栈)

[题目链接] http://poj.org/problem?id=3415 [题目大意] 求出两个字符串长度大于k的公共子串的数目. [题解] 首先,很容易想到O(n2)的算法,将A串和B串加拼接符相连, 做一遍后缀数组,把分别属于A和B的所有后缀匹配,LCP-k+1就是对答案的贡献, 但是在这个基础上该如何优化呢. 我们可以发现按照sa的顺序下来,每个后缀和前面的串的LCP就是区间LCP的最小值, 那么我们维护一个单调栈,将所有单调递减的LCP值合并, 保存数量和长度,对每个属于B串的后缀更新

[bzoj3238]差异(后缀数组+单调栈)

显然我们可以先把len(Ti)+len(Tj)的值先算出来,再把LCP减去.所有len(Ti)+len(Tj)的值为n*(n-1)*(n+1)/2,这个随便在纸上画一画就可以算出来的. 接下来问题就是如何把LCP减去.我们先用后缀数组把height求出来,当有一段区间l~r,height[i]为height[l]~height[r]中的最小值,那么在l~r当中随便取两个后缀,他们的LCP则都是height[i],这个很好理解吧.那么l~r这个区间里有(l-i+1)*(r-i+1)对后缀,所以我们

POJ 3415:后缀数组+单调栈优化

题意很简单,求两个字符串长度大于等于K的子串个数 一开始还是只会暴力..发现n^2根本没法做...看了题解理解了半天才弄出来,太弱了... 思路:把两个字符串连接后做一次后缀数组,求出height 暴力的想法自然是枚举第一个子串的起始位置i和第二个子串的起始位置j,肯定会T的 看了题解才知道有单调栈这种有优化方法.. 将后缀分为A组(起始点为第一个字符串).B组 设符合要求的lcp长度为a,则其对答案的贡献为a-k+1(长度为k~a的都是符合要求的) 一开始这里我也是有疑问的,比如说k=1,aa

POJ 3415 后缀数组+单调栈

题目大意: 给定A,B两种字符串,问他们当中的长度大于k的公共子串的个数有多少个 这道题目本身理解不难,将两个字符串合并后求出它的后缀数组 然后利用后缀数组求解答案 这里一开始看题解说要用栈的思想,觉得很麻烦就不做了,后来在比赛中又遇到就后悔了,到今天看了很久才算看懂 首先建一个栈,从栈底到栈顶都保证是单调递增的 我们用一个tot记录当前栈中所有项和一个刚进入的子串匹配所能得到的总的子串的数目(当然前提是,当前进入的子串height值比栈顶还大,那么和栈中任意一个子串匹配都保持当前栈中记录的那时

POJ 3415 Common Substrings(长度不小于k 的公共子串的个数--后缀数组+单调栈优化)

题意:给定两个字符串A 和B,求长度不小于k 的公共子串的个数(可以相同). 样例1: A="xx",B="xx",k=1,长度不小于k 的公共子串的个数是5. 样例2: A ="aababaa",B ="abaabaa",k=2,长度不小于k 的公共子串的个数是22. 思路: 如果i后缀与j后缀的LCP长度为L, 在L不小于K的情况下, 它对答案的贡献为L - K + 1. 于是我们可以将两个串连起来, 中间加个奇葩的分隔符