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] 优秀的拆分 的相关文章

  •     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
  • phpStorm 2016.1 最新版激活方法

    新版激活方法 1 在线激活 最新 http 123 206 193 241 1017 http www 0 php com 1017 xff08 可用 xff0c 更新于 20170621 xff09 http idea singee77
  • Luogu 3631 [APIO 2011] 方格染色

    传送门思路参考代码细节 传送门 思路 很不错的一道题 xff0c 用到的东西不深 xff0c 但是要想到确实需要一定思维 一开始我想的是动态规划 xff0c 发现如果要设状态需要知道一个格子左边 xff0c 上边和左上边三个格子的状态 然后
  • Luogu 3634 [APIO 2012] 守卫

    传送门思路正解参考代码 传送门 思路 感觉自己越来越笨了 首先 xff0c 很明显这道题需要把没有看到忍者的区间给删去 xff0c 可以用前缀和 O n O n 处理 xff0c 然后对没有删去的地方重新标号 重新标号时 xff0c 需要对
  • Luogu 1552 [APIO 2012] 派遣

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

    传送门思路参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 看到这道题就想到了二分答案找负环 xff0c 但是怎么做呢 xff1f 完全不会 唉 xff0c 我太弱啦 xff01 先注意题目中说可以重复经过点和边 x
  • Luogu 2146 [NOI 2015] 软件包管理器

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

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

    传送门思路参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 当年网同这道题还拿了 16 16 分 xff0c 现在一分都不会做了 xff0c 唉 xff0c 我太弱啦 xff01 这道题其实是很不错的 x
  • 记录生活,记录学习----我的2016

    过着2017年的日子 xff0c 思考着2016年人生的变化 xff0c 或许 xff0c 最大的变化是懂得记录学习 xff0c 记录生活吧 2016年 xff0c 博客进入了我的生活 xff0c 从年初的寥寥数篇博客 xff0c 到现在C
  • 我的2016--"狗血"

    偶然看到了CSDN的 我的2016 主题征文活动 xff0c 突然感慨一番 xff0c 今年又快结束了 xff0c 而我这一年的经历 xff0c 可以浓缩为两个字 xff1a 狗血 然而 xff0c 我能用上如此不羁的词汇 xff0c 并未
  • 2016.2.27 Px4 flow分析

    Px4 flow 分析 代码地址 https github com PX4 Flow 主要来分析最后一个函数 compute flow 原版代码的光流算法主要是使用 hist 直方图算法 xff0c 这段代码主要可以分成两部分来看 xff0
  • 2016 CSDN最佳博客(Android)

    无意中在CSDN上看见了今年的十佳博客 xff0c 虽然现在还没有分出伯仲 xff0c 但是结果大概已知 xff0c 其中看了几篇文章 xff0c 感触挺深 xff0c 故把几大博客整理下来 xff0c 一方面方便广大博友 xff0c 另一
  • 2016

    2016 最近 xff0c 许多朋友兴起总结2016了 xff0c 看得我心痒 xff0c 心热 我自己不禁也总结起来了 别人的总结要么是 2016XXXX 要么是 2016OOOO 我苦思2秒 xff0c 却想不起一个标题来 xff0c
  • 【2015-2016,我在路上】

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

    年年有计划 xff0c 岁岁有复盘 xff0c 今天是2016年的最后一天 我也来回忆一下我的2016年 xff0c 展望一下2017年 记得去年的跨年是和几个朋友在一块儿的过的 xff0c 记得当时玩儿了麻将 xff0c 我输了 xff0
  • 2016总结

    欲言又止 xff1a 每年的年终总结是要在新年之前发表在博客上 xff0c 今年的年终总结拖到现在完成 xff0c 我也是服自己 这里要感谢我的高中好友 64 万学清同学 xff0c 在我去年微信发表的有关年终总结的朋友圈下 xff0c 催
  • 【系统篇 / 域】❀ 06. Windows10 加入域 ❀ Windows Server 2016

    简介 众所周知 Windows Server 2016 与其它版本不同的地方就是支持 Windows10 加入域服务了 修改 DNS Windows10 加入域之前 需要把网卡的DNS指向域服务器 在Windows10系统中 鼠标右击右下角

随机推荐

  • 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 可以枚举一个中间点