P2463 [SDOI2008]Sandy的卡片

题目链接 \(Click\) \(Here\)

真的好麻烦啊。。事实证明,理解是理解,一定要认认真真把板子打牢,不然调锅的时候真的会很痛苦。。(最好是八分钟能无脑把\(SA\)码对的程度\(QAQ\))

这个题最开始我想的是\(RMQ\)遍历每一个子区间,但是意识到复杂度是\(O(N^2)\)然后就\(GG\)了。怎么说呢,后缀数组和二分似乎是很常见的组合(和莫队也是?),这个题只需要在\(height\)数组里二分\(lcp\)长度即可,\(check\)函数里面处理一下,要让区间内所有原串都有至少一个子串。

#include <bits/stdc++.h>
using namespace std;

const int N = 200010;

int s[N], id[N];
int n, m, num, len, tot = 10000;
int sa[N], tp[N], rk[N], _rk[N], bin[N], height[N];

void get_height (int n) {
    int k = 0;
    for (int i = 1; i <= n; ++i) {
        if (k != 0) k--;
        int j = sa[rk[i] - 1];
        while (s[i + k] == s[j + k]) k++;
        height[rk[i]] = k;
    }
}

void base_sort (int n, int m) {
    for (int i = 0; i <= m; ++i) bin[i] = 0;
    for (int i = 1; i <= n; ++i) bin[rk[tp[i]]]++;
    for (int i = 1; i <= m; ++i) bin[i] += bin[i - 1];
    for (int i = n; i >= 1; --i) sa[bin[rk[tp[i]]]--] = tp[i];
}

void suffix_sort (int n, int m) {
    for (int i = 1; i <= n; ++i) {
        rk[i] = s[i];
        tp[i] = i;
    }
    base_sort (n, m);
    for (int w = 1; w <= n; w <<= 1) {
        int cnt = 0;
        for (int i = n - w + 1; i <= n; ++i) {
            tp[++cnt] = i;
        }
        for (int i = 1; i <= n; ++i) {
            if (sa[i] > w) {
                tp[++cnt] = sa[i] - w;
            }
        }
        base_sort (n, m);
        memcpy (_rk, rk, sizeof (rk));
        rk[sa[1]] = cnt = 1;
        for (int i = 2; i <= n; ++i) {
            rk[sa[i]] = _rk[sa[i]] == _rk[sa[i - 1]] && _rk[sa[i] + w] == _rk[sa[i - 1] + w] ? cnt : ++cnt;
        }
        if (cnt == n) break;
        m = cnt;
    }
}

bool vis[1010]; int sta[N], top = 0;

bool can_use (int l) {
    while (top) vis[sta[top--]] = false;
    for (int i = 1; i <= len; ++i) {
        if (height[i] < l) {
            while (top) vis[sta[top--]] = false;
        }
        if (!vis[id[sa[i]]]) {
            vis[id[sa[i]]] = true;
            sta[++top] = id[sa[i]];
            if (top == n) return true;
        }
    }
    return false;
}

int main () {
    cin >> n;
    int ban = 2000;
    for (int i = 1; i <= n; ++i) {
        cin >> m;
        for (int j = 1; j <= m; ++j) {
            cin >> s[++len]; //把所有的字符串整合到一个里
            id[len] = i; // 表明主权(len号后缀(的lcp)属于串i)
        }
        s[++len] = ++ban; //隔开
    }
    for (int i = len; i >= 1; --i) {
        s[i] = s[i] - s[i - 1] + 4000;
    }
    suffix_sort (len, 10000);
    get_height (len);
    int l = 0, r = len;
    while (l < r) {
        int mid = (l + r + 1) >> 1;
        if (can_use (mid)) {
            l = mid;
        } else {
            r = mid - 1;
        }
    }
    cout << l + 1 << endl;
}

原文地址:https://www.cnblogs.com/maomao9173/p/10438209.html

时间: 2024-10-09 06:22:07

P2463 [SDOI2008]Sandy的卡片的相关文章

bzoj4698 / P2463 [SDOI2008]Sandy的卡片

P2463 [SDOI2008]Sandy的卡片 直接二分长度暴力匹配....... 跑的还挺快 (正解是后缀数组的样子) 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 void read(int &x){ 6 char c=getchar();x=0; 7 while(c<'0'||c>'9')c=getchar(); 8 w

BZOJ 4698: Sdoi2008 Sandy的卡片

4698: Sdoi2008 Sandy的卡片 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 225  Solved: 95[Submit][Status][Discuss] Description Sandy和Sue的热衷于收集干脆面中的卡片.然而,Sue收集卡片是因为卡片上漂亮的人物形象,而Sandy则是为了积 攒卡片兑换超炫的人物模型.每一张卡片都由一些数字进行标记,第i张卡片的序列长度为Mi,要想兑换人物模型 ,首先必须要集够N张卡片,对

[Sdoi2008]Sandy的卡片

[Sdoi2008]Sandy的卡片 题目 Sandy和Sue的热衷于收集干脆面中的卡片.然而,Sue收集卡片是因为卡片上漂亮的人物形象,而Sandy则是为了积攒卡片兑换超炫的人物模型.每一张卡片都由一些数字进行标记,第i张卡片的序列长度为Mi,要想兑换人物模型,首先必须要集够N张卡片,对于这N张卡片,如果他们都有一个相同的子串长度为k,则可以兑换一个等级为k的人物模型.相同的定义为:两个子串长度相同且一个串的全部元素加上一个数就会变成另一个串.Sandy的卡片数远远小于要求的N,于是Sue决定

SDOI2008 Sandy的卡片( 后缀数组 )

求出后缀数组, 然后二分答案, 对height数组分组检验答案. 时间复杂度O(|S| log|S|) -------------------------------------------------------------------------------- #include<cstdio> #include<cctype> #include<cstring> #include<algorithm> using namespace std; const

luogu 2463 [SDOI2008]Sandy的卡片 kmp || 后缀数组 n个串的最长公共子串

题目链接 Description 给出\(n\)个序列.找出这\(n\)个序列的最长相同子串. 在这里,相同定义为:两个子串长度相同且一个串的全部元素加上一个数就会变成另一个串. 思路 参考:hzwer. 法一:kmp 在第一个串中枚举答案串的开头位置,与其余\(n-1\)个串做\(kmp\). 法二:后缀数组 将\(n\)个串拼接起来.二分答案\(len\),将\(height\)分组,\(check\)是否有一组个数\(\geq len\)且落在\(n\)个不同的串中. 注意:\(n\)个串

BZOJ4698 [SDOI2008] Sandy的卡片 - 后缀数组,二分

题意:求在N个串中都出现的最长子串 的长度 很容易想到二分转化为判定性问题.考虑长度M,我们按照长度M进行分组,每个组内进行答案验证,即检查组内是否有N个串的后缀都出现. 时间复杂度O(LlogM) 其实是个经典题. 二分时候记得初始化! 1 #include <bits/stdc++.h> 2 using namespace std; 3 4 struct SA { 5 int str[2100005]; 6 int x[2100005],y[2100005],u[2100005],v[21

[Luogu2463][SDOI2008]Sandy的卡片

BZOJ权限题qwq Luogu sol "两个子串长度相同且一个串的全部元素加上一个数就会变成另一个串" 其实就是差分一波以后完全相同 所以对输入的数据进行差分,同时记一下每一个位置是属于哪个串的. 记得在串与串中间加入一个没有出现的字符. 求出SA后二分答案\(mid\),问题变成:\(Height\)数组里是否存在一个大于等于\(mid\)的连续段使每个串都在里面出现过. 开桶记录.记一个\(id\)避免每次对桶的清空. code #include<cstdio> #

【SDOI2008】Sandy的卡片

题目描述 Sandy和Sue的热衷于收集干脆面中的卡片. 然而,Sue收集卡片是因为卡片上漂亮的人物形象,而Sandy则是为了积攒卡片兑换超炫的人物模型. 每一张卡片都由一些数字进行标记,第i张卡片的序列长度为Mi,要想兑换人物模型,首先必须要集够N张卡片,对于这N张卡片,如果他们都有一个相同的子串长度为k,则可以兑换一个等级为k的人物模型.相同的定义为:两个子串长度相同且一个串的全部元素加上一个数就会变成另一个串. Sandy的卡片数远远小于要求的N,于是Sue决定在Sandy的生日将自己的卡

[SDOI2008][luogu2463] Sandy的卡片 [kmp]

题面 传送门 思路 这道题里面有三个主要问题: 1.由"数值相等"变成了"加上一个整数以后数值相等"(减去等价于加负数) 2.由"最多匹配多少位(从第一位开始)"变成了从"任意一位开始匹配" 3.由"单文本串"变成了"多文本串",而且是文本串内部自己匹配 我们按照难度顺序(3-1-2)来依次解决他们 问题三:多文本串 这个问题是最简单的,因为每个串长度不超过100,最多1000个串 所以