Luogu 1117 [NOI 2016] 优秀的拆分

2023-05-16

          • 传送门
          • 思路
          • 利用后缀数组解决重复子串问题
          • 注意事项
          • 参考代码

传送门
思路

  唉,我太弱了,什么都不会,连暴力都想不到,唉,我太弱啦!

  考虑暴力法,可以枚举一个中间点,左边是 AA,右边是 BB,显然一个中间点对答案的贡献为左边 AA 的个数乘以右边 BB 的个数。考虑用哈希,则我们立马得到一个时间复杂度为 O(n2) O ( n 2 ) 的算法。这样就有了 95 95 分……唉,这个我都没有秒杀,我太弱啦!

  如果我们想要基于以上做法进行改进,显然我们的任务变成了找 AA 型字符串出现的位置,如果找到了问题就解决了。但是我太弱了,什么都不会,所以凉了。

  唉,我太弱了,基础不牢,地动山摇,要用到后缀数组处理重复子串的东西,但是那个东西我正好弃掉了,还是找个时间学了来再看吧……

利用后缀数组解决重复子串问题

  先盗一张经典的图:

  什么意思呢?这张图讲述的其实是后缀数组解决重复子串问题的通用方法。

  我们先枚举重复子串的长度 L L ,然后我们考虑字符 str0,strL,str2L,str3L(称它们为关键点)显然,如果这个重复子串重复了 k k 次,以上字符就会有 k 个被包含在那个重复子串中,且对于相邻的两个关键点,往前和往后是相同的(因为是重复子串嘛)

  例如:xxxaabbaabbxxx,长度为 4 4 的重复子串为 aabb。我们的关键点将字符串分成了:

xxxa|abba|abbx|xx

  关键点为 x(0)a(4)a(8)和 x(12)。对于第二个关键点和第三个关键点,它们往后能够匹配长度为 3 的字符串(abb),往前能匹配长度为 1 1 的字符串(a),拼起来就得到了长度为 4 的字符串 aabb。

  也就是说,我们枚举相邻的关键点,然后向前向后匹配,如果匹配总长度大于等于 k k ,设为 l,相当于我们在此之间找到了一个或多个重复了两次的子串。剩下的部分怎么做应该就明白了,留给你们思考。

  枚举长度的时间复杂度是 O(n) O ( n ) 的,匹配(求 LCP)的时间复杂度是 O(1) O ( 1 ) 的(用 RMQ),由于关键点数目为 O(ni) O ( n i ) ,因此总的时间复杂度为 O(nlogn) O ( n log ⁡ n )

注意事项

  唉,我太弱了,好不容易写了一份过了的代码 QAQ。我的实现细节是: i i 0 开始,代表左边的关键点, j j l 开始,代表与 i i 相邻的右边的关键点,退出条件是 jn(即访问 str[j] 越界就退出)。向前匹配(Forward)时,从第 i i 个位置和第 j 个位置进行匹配。向后匹配(Backward)时,也从第 i i 个位置和第 j 个位置进行匹配,这样可以防止越界,最后再相应地减去 1 1 即可。至于那些加一减一的问题,额,OI 基本功,举几个例子看看到底该加几就好了(虽然这个过程很痛苦,但是在 OI 生涯中似乎一直都没有避免)。

  另外注意求 LCP 时,是在 SA 上做 RMQ,并且要特判自己和自己求 LCP 的情况!并且在 SA 上是求的 l<ir 的最小值!(看不懂?又没说这句话是给你看的)

参考代码
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <cassert>
#include <cctype>
#include <climits>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <stack>
#include <queue>
#include <deque>
#include <map>
#include <set>
#include <bitset>
#include <list>
#include <functional>
#define loop register int
typedef long long LL;
typedef unsigned long long ULL;
using std::cin;
using std::cout;
using std::endl;
typedef LL INT_PUT;
INT_PUT readIn()
{
    INT_PUT a = 0; bool positive = true;
    char ch = getchar();
    while (!(ch == '-' || std::isdigit(ch))) ch = getchar();
    if (ch == '-') { positive = false; ch = getchar(); }
    while (std::isdigit(ch)) { a = a * 10 - (ch - '0'); ch = getchar(); }
    return positive ? -a : a;
}
void printOut(INT_PUT x)
{
    char buffer[20]; int length = 0;
    if (x < 0) putchar('-'); else x = -x;
    do buffer[length++] = -(x % 10) + '0'; while (x /= 10);
    do putchar(buffer[--length]); while (length);
    putchar('\n');
}

const int maxn = 30005;
int n;
char str[maxn];

#define RunInstance(x) delete new x
struct brute
{
    ULL hashVal[maxn];
    ULL power[maxn];
    brute()
    {
        power[0] = 1;
        for (loop i = 1; i <= n; i++)
            power[i] = power[i - 1] * 131;

        hashVal[0] = 0;
        for (loop i = 1; i <= n; i++)
            hashVal[i] = hashVal[i - 1] * 131 + str[i - 1];

        LL ans = 0;
        for (int i = 2; i <= n - 2; i++)
        {
            int cnt1 = 0, cnt2 = 0;
            for (int l = 1; i - (l << 1) >= 0; l++)
            {
                if (hashVal[i] - hashVal[i - l] * power[l] ==
                    hashVal[i - l] - hashVal[i - (l << 1)] * power[l])
                    cnt1++;
            }
            for (int l = 1; i + (l << 1) <= n; l++)
            {
                if (hashVal[i + l] - hashVal[i] * power[l] ==
                    hashVal[i + (l << 1)] - hashVal[i + l] * power[l])
                    cnt2++;
            }
            ans += cnt1 * cnt2;
        }
        printOut(ans);
    }
};
struct work
{
    struct SAHelper
    {
        int SA[maxn];
        int Rank[maxn];
        int Height[maxn];
        void GetSA()
        {
            static int buf[maxn];
            static int x[maxn], y[maxn];

            int buf_size = 26;
            int *rank = x, *SA_second = y;
            for (int i = 0; i < n; i++)
                rank[i] = str[i] - 'a';

            for (int i = 0; i < buf_size; i++) buf[i] = 0;
            for (int i = 0; i < n; i++) buf[rank[i]]++;
            for (int i = 1; i < buf_size; i++) buf[i] += buf[i - 1];
            for (int i = n - 1; ~i; i--) SA[--buf[rank[i]]] = i;

            for (int k = 1; k <= n; k <<= 1)
            {
                int t = 0;
                for (int i = n - k; i < n; i++)
                    SA_second[t++] = i;
                for (int i = 0; i < n; i++)
                    if (SA[i] >= k) SA_second[t++] = SA[i] - k;

                for (int i = 0; i < buf_size; i++) buf[i] = 0;
                for (int i = 0; i < n; i++) buf[rank[SA_second[i]]]++;
                for (int i = 1; i < buf_size; i++) buf[i] += buf[i - 1];
                for (int i = n - 1; ~i; i--) SA[--buf[rank[SA_second[i]]]] = SA_second[i];

                int* oldRank = rank;
                std::swap(rank, SA_second);
                rank[SA[0]] = 0;
                t = 1;
                for (int i = 1; i < n; i++)
                    rank[SA[i]] = (oldRank[SA[i]] == oldRank[SA[i - 1]] &&
                        SA[i] + k < n && SA[i - 1] + k < n &&
                        oldRank[SA[i] + k] == oldRank[SA[i - 1] + k])
                    ? t - 1 : t++;
                if (t >= n) break;
                buf_size = t;
            }
        }
        void GetHeight()
        {
            for (int i = 0; i < n; i++)
                Rank[SA[i]] = i;

            int same = 0;
            for (int i = 0; i < n; i++)
            {
                if (same) same--;
                if (Rank[i])
                {
                    int pre = SA[Rank[i] - 1];
                    while (i + same < n && pre + same < n &&
                        str[i + same] == str[pre + same])
                        same++;
                }
                else
                    same = 0;
                Height[Rank[i]] = same;
            }
        }

        int k;
        int minVal[16][maxn];
        int Log[maxn];
        void init()
        {
            for (int i = 0; i < n; i++)
                minVal[0][i] = Height[i];
            k = 0;
            while (1 << (k + 1) < n) k++;
            for (int i = 1; i <= k; i++)
                for (int j = 0; j + (1 << i) - 1 < n; j++)
                    minVal[i][j] = std::min(minVal[i - 1][j],
                        minVal[i - 1][j + (1 << (i - 1))]);

            for (register int i = 0; i <= n; i++)
            {
                register int t = 0;
                while (1 << (t + 1) < i) t++;
                Log[i] = t;
            }
        }
        int LCP(int a, int b)
        {
            if (a == b) return n - a;
            a = Rank[a];
            b = Rank[b];
            if (a > b) std::swap(a, b);
            a++;
            register int length = b - a + 1;
            register int t = Log[length];
            return std::min(minVal[t][a], minVal[t][b - (1 << t) + 1]);
        }
    } sa1, sa2;

    int Left[maxn], Right[maxn];

    work() : Left(), Right()
    {
        sa1.GetSA();
        sa1.GetHeight();
        sa1.init();
        std::reverse(str, str + n);
        sa2.GetSA();
        sa2.GetHeight();
        sa2.init();

        for (int l = 1; l <= (n >> 1); l++)
        {
            for (int i = 0, j = l; j < n; i += l, j += l)
            {
                int Forward = std::min(sa1.LCP(i, j), l);
                int Backward = std::min(sa2.LCP(n - 1 - i, n - 1 - j), l);
                int len = Forward + Backward - l - 1;
                if (len >= 0)
                {
                    Left[j + Forward - 1 - len]++;
                    Left[j + Forward]--;
                    Right[i - Backward + 1]++;
                    Right[i - Backward + 1 + len + 1]--;
                }
            }
        }
        for (int i = 1; i < n; i++)
            Left[i] += Left[i - 1];
        for (int i = 1; i < n; i++)
            Right[i] += Right[i - 1];
        LL ans = 0;
        for (int i = 1; i < n - 2; i++)
            ans += (LL)Left[i] * Right[i + 1];
        printOut(ans);
    }
};

void run()
{
    int T = readIn();
    while (T--)
    {
        scanf("%s", str);
        n = strlen(str);
        RunInstance(work);
    }
}


int main()
{
    run();
    return 0;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Luogu 1117 [NOI 2016] 优秀的拆分 的相关文章

  • office 2019 & visio 2016安装

    安装包和相关工具 xff1a visio 2016 专业版 xff1a office 2019 xff1a 相关工具 xff1a 安装步骤 xff1a visio 2016 和 office 2019 安装存在问题 xff0c 笔者安装遇到
  •     2016 年 高等工程数学 期末试题

    2016 年 高等工程数学 期末试题
  • Win10 LTSB 2016 激活

    以管理员权限打开 命令提示符 xff0c 输入一代码 一行一行复制 xff0c 回车 slmgr skms kms digiboy ir slmgr ato 转载于 https www cnblogs com kjcy8 p 1123701
  • sql server 2016 json 解析方法

    前几天发现了sql server 2016支持了json 项目需要所以安装了 用了一下 方便了很多 xff0c 写一下小笔记方便日后查看 xff0c 也希望各位大神指正共同学习 sql server 2016 安装图解网上很多 xff0c
  • CSP考试 2016年04月第3题 路径解析 C++实现

    表示本目录 xff0c 例如 d1 f1 指定的就是 d1 f1 如果有多个连续的 出现 xff0c 其效果等同于一个 绝对路径 xff1a 以 符号开头 xff0c 表示从根目录开始构建的路径 相对路径 xff1a 不以 符号开头 xff
  • 2016 Team Training #21 Gym 100952 A D E F J

    A 水题 题意 xff1a 两个人的时间分别是时 xff0c 分 xff0c 秒输入 xff0c 也就是让我们输出谁时间最早呗 思路 xff1a 没有思路直接上 xff0c 看手速了 xff08 我敲代码速度慢 xff09 代码如下 xff
  • Luogu 3632 [APIO 2011] 寻路

    传送门正解参考代码 传送门 正解 暴力连边跑最短路就好了 xff0c 只不过代码太长太难写啦 xff01 参考代码 span class hljs preprocessor include lt cstdio gt span span cl
  • Luogu 1552 [APIO 2012] 派遣

    传送门思路参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 题读错了两次 xff0c 一开始读成了一个一般的背包 xff0c 然后读成了一个价值和花费相同的背包 xff0c 最后才发现原来是一个价值为 1
  • Luogu 2168 [NOI 2015] 荷马史诗

    传送门思路参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 连哈夫曼树都不会 这道题就是一个 k k 叉哈夫曼树 题目要求满足两个条件 一是代价最小 二是最长长度最小 最长长度最小很好解决 只需要优先合并
  • NOI 2018 游记

    Day 2Day 1Day 0Day 1Day 2Day 3Day 4Day inftyDay 5 Day 2 昨天打完了最后一个一场模拟赛 xff0c 又垫底啦 xff01 本来之前我很少垫底的 xff0c 不知道为什么最后四场模拟赛一直
  • 致敬我奋起直追的2016

    前言 其实当用奋起直追这个词语形容我的2016时 xff0c 自己一度怀疑是不是配得上这个词语 虽然2016成长了不少 xff0c 但是依然没有达到我想要的效果 在学习过程中不断出现越学越倒退的感觉 还偶尔会出现一些恐惧感 不过庆幸的是 x
  • 一个脚本打比赛之SMP WEIBO 2016

    一个脚本打比赛之SMP WEIBO 2016 前言 xff1a 如何对用户进行精准画像是社交网络分析的基础问题 本文就如何对weibo用户网络提取特征发表一点小的想法 xff0c 还请尽管拍砖 数据来源 xff1a SMP WEIBO 20
  • leetcode题解日练--2016.6.17

    编程新手 xff0c 尽量保证每天至少3道leetcode题 xff0c 仅此记录学习的一些题目答案与思路 xff0c 尽量用多种思路来分析解决问题 xff0c 不足之处还望指出 今日题目 xff1a 1 罗马数字转整数 xff1b 2 找
  • Windows Server 2016 路由和远程访问

    本次实验是将Windows Server 2016 配置成一个路由器 xff0c 为此网络上的客户端和服务器启用多重协议LAN到LAN xff0c LAN到WAN xff0c 虚拟专用网络和网络地址转换路由服务 使用路由和远程访问需配置下列
  • 【2015-2016,我在路上】

    前言 xff1a 每天 xff0c 每时 xff0c 每分 xff0c 时光的步伐永远不会停止 xff0c 当我提起笔 xff0c 写下的这一瞬间 xff0c 时间又是一年 xff0c 一年的时光 xff0c 在没逝去时 xff0c 感觉很
  • 盘点2016

    年年有计划 xff0c 岁岁有复盘 xff0c 今天是2016年的最后一天 我也来回忆一下我的2016年 xff0c 展望一下2017年 记得去年的跨年是和几个朋友在一块儿的过的 xff0c 记得当时玩儿了麻将 xff0c 我输了 xff0
  • 华为2016笔试-扑克牌大小比较游戏 python接法

    这几天刷题 xff0c 发现该题没有python的程序 xff0c 正好在学python xff0c 尝试写了下 xff0c 没有用任何库 xff0c 写的不好 xff0c 有很多改进的地方 基于python3 7 扑克牌游戏大家应该都比较
  • 2016 OWASP Mobile TOP 10 中文版

    M1 平台使用不当 这个类别包括平台功能的滥用 或未能使用平台的安全控制 它可能包括 Android 的意图 intent 平台权限 TouchID 的误用 密钥链 KeyChain 或是移动操作系统中的其他一些安全控制 有几种方式使移动应
  • 在Windows Server 2016 Hyper-V中开启嵌套虚拟化(NestedVM)

    早期如果我们想做Hyper V功能测试 例如Hyper V Cluster或者Hyper V Replica时至少使用两台物理机器实现 作为大众屌丝没那么多钱购买机器怎么办 嵌套虚拟化 嵌套虚拟化 顾名思义 即在虚拟机中运行虚拟机 该技术最
  • [NOI 2015复习][BZOJ 1509][NOI 2003]逃学的小孩(树的直径)

    题目链接 http www lydsy com JudgeOnline problem php id 1509 题目大意 要从一棵树中找出三个点 X Y Z X Y Z 使得 min dis A C dis B C dis A B min

随机推荐

  • Luogu 3638 [APIO 2013] 机器人

    传送门思路正解参考代码关于 SPFA 传送门 思路 n n 这么小 会不会是搜索题 稍有经验的我直接否定了这个结论 仔细读题并分析样例 发现原来一个位置可以有多个机器人 且机器人行走的时候无视其它机器人 那这个就是一张图啊 可以将这张图预处
  • Luogu 3647 [APIO 2014] 连珠线

    传送门思路参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 又看错题了 题目中说一个新的珠子和一个已经添加的珠子连接起来 xff0c 我没有看到 xff0c 然后就凉了 立个 flag xff1a 已经连续看错五题了 xff0c
  • 【转】mingw64的安装方法

    转自 xff1a http write blog csdn net postlist mingw64的安装方法 1 下载ming w64 http sourceforge net projects mingw w64 files or x8
  • Luogu 3645 [APIO 2015] 雅加达的摩天楼

    传送门思路正解参考代码Update 传送门 思路 唉 xff0c 我太弱了 xff0c 我都看出来要分块了 xff0c 就是做不来 不过终于把题读对了 先来看子任务三怎么做 显然可以有一个 O m 2 O m 2
  • Luogu 3644 [APIO 2015] 八邻旁之桥

    传送门思路当 k 61 2 时参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 题也做不来 很明显这道题先要把不过河的人排除了 xff0c 剩下的都是要过河的 当 k 61 1 k 61 1 时 xff0
  • Luogu 3646 [APIO 2015] 巴厘岛的雕塑

    传送门总结 APIO 2015思路参考代码总结 传送门 总结 APIO 2015 争取今天做完一套 QAQ T1 我最多之能想到从高位向低位做 xff0c 然后就完全不会了 xff1b T2 我想到了分情况讨论 xff0c 但是没有建图成功
  • UOJ 2016 [APIO 2016] Gap

    传送门思路参考代码交互题 交互题大致形式Windows 平台下 xff08 Dev C 43 43 xff09 Ubuntu 平台下 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 题也做不来 这道题简直就是利用
  • CF 940F Machine Learning

    传送门题目大意思路参考代码Remarks 传送门 题目大意 给你一个数组 a 1 n n 10 5 a 1 n
  • CF 976D Degree Set

    传送门题目大意思路参考代码总结 传送门 题目大意 给你一个长度为 n n 的正整数序列 d 1 d 2 d n d1 d2 dn xff08 d 1 lt d 2 lt lt d n
  • Luogu 3778 [APIO 2017] 商旅

    传送门思路参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 看到这道题就想到了二分答案找负环 xff0c 但是怎么做呢 xff1f 完全不会 唉 xff0c 我太弱啦 xff01 先注意题目中说可以重复经过点和边 x
  • CF 963E Circles of Waiting

    传送门题目大意思路参考代码 传送门 题目大意 在平面直角坐标系上 xff0c 有一个神奇的点 xff0c 一开始在 0 0 0 0 每秒钟这个点都会随机移动 xff1a 如果它在 x y
  • CF 976F Minimal k-covering

    传送门题目大意 输入格式输出格式 思路参考代码 传送门 题目大意 给你一张二分图 G 61 U V E G 61 U V
  • CF 963A Alternating Sum

    传送门思路参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 好不容易做得来一道题 xff0c 还是 A 题 xff08 所以不要瞧不起 A 题 xff09 xff0c 结果还写错了 xff08 不知道为什
  • iOS中瀑布流布局详解

    前段时间在逛淘宝的时候发现淘宝的商品界面的布局是瀑布流 我记得明明之前不是瀑布流的 x1f611 刚好手上活忙完了 xff0c 写了一个瀑布流的布局 xff0c 简单的封装了下 xff0c 以便日后使用 x1f60f 其实说到底瀑布流也就是
  • Luogu 2146 [NOI 2015] 软件包管理器

    传送门思路参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 好不容易遇到一道傻逼题 xff0c 又出了个傻逼错误 xff0c 爆得只剩 30 30 分了 唉 xff0c 我太弱啦 xff01 显然 xff
  • Luogu 2150 [NOI 2015] 寿司晚宴

    传送门思路对于 30 30 30 的数据对于 100 100 100 的数据参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 完全做不来 xff0c 连暴力都打不来 主要好像是因为我从来没有做过以质因子为
  • Luogu 3649 [APIO 2014] 回文串

    传送门思路Manacher 算法 特殊字符回文半径算法与实现本质不同的回文串个数 正解参考代码总结 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 这道题各路神仙都说是模板题 xff0c 但我觉得完全不可做 xf
  • Luogu 2168 [NOI 2015] 荷马史诗

    传送门思路参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 连哈夫曼树都不会 这道题就是一个 k k 叉哈夫曼树 题目要求满足两个条件 一是代价最小 二是最长长度最小 最长长度最小很好解决 只需要优先合并
  • Luogu 2178 [NOI 2015] 品酒大会

    传送门思路参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 做了两个星期的题 xff0c 自己做出来的才只有这一道 xff0c 唉 xff0c 我太弱啦 xff01 我们考虑第一问怎么做 题目中相似的概念
  • Luogu 1117 [NOI 2016] 优秀的拆分

    传送门思路利用后缀数组解决重复子串问题注意事项参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 连暴力都想不到 xff0c 唉 xff0c 我太弱啦 xff01 考虑暴力法 xff0c 可以枚举一个中间点