zoukankan      html  css  js  c++  java
  • CF319D

    CF319D

    这么棒的题居然没有人写。

    很类似的题:P1117(也是黑色的)(和我写的这道题的的博客

    题意分析

    题目意思是告诉我们一个字符串,然后让我们从短到长依次缩短相邻且相同的字符串(例如abcabc变为abc)。

    我们枚举缩短的字符串的长度(len),然后判断有没有长度为(2 * len)的相邻且相同的字符串。一个常规的方法是我们在字符串上每隔(len)长度放置一个观察点,对于两个观察点,对相邻两个观察点做最长公共前缀(LCP)和最长公共后缀(LCS),显然将前缀和后缀加起来而组成的字符串即为在两点上的相同串。且若相邻两串的(LCP+LCS>=len),则显然他们是相连的(虽然(LCP+LCS>len)的情况中字符串长度长过len,但是出于贪心考虑,我们会将LCS中多余的部分切去)。这是删去其中的左侧串就可以了(因为右侧串可能会继续匹配)。当然删除之后我们需要重新构造字符串和哈希串。

    对于求LCP和LCS的部分,我们可以使用字符串哈希+二分的方法,二分LCP或LCS的长度并且判断是否相同。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int mod = 1e9 + 7;
    
    long long a[50010], w[50010], p[50010];
    char s[50010];
    
    int get(int l, int r)
    { //计算字符串中l~r段的哈希值
        return ((w[r] - w[l - 1] * p[r - l + 1]) % mod + mod) % mod;
    }
    
    int n, now;
    
    int lcp(int x, int y) //求最长前缀
    {
        int l = 0, r = now;
        while (l < r)
        {
            int mid = l + r + 1 >> 1;
            if (get(x - mid + 1, x) == get(y - mid + 1, y))
                l = mid;
            else
                r = mid - 1;
        }
        return l;
    }
    
    int lcs(int x, int y) //求最长后缀
    {
        int l = 0, r = n - y + 1;
        while (l < r)
        {
            int mid = l + r + 1 >> 1;
            if (get(x, x + mid - 1) == get(y, y + mid - 1))
                l = mid;
            else
                r = mid - 1;
        }
        return l;
    }
    
    int main()
    {
        scanf("%s", s + 1);
        n = strlen(s + 1);
        p[0] = 1;
        for (int i = 1; i <= n; i++)
        { //字符串哈希
            p[i] = p[i - 1] * 31 % mod;
            w[i] = (w[i - 1] * 31 + s[i] - 'a') % mod;
        }
        for (int len = 1; len <= n; len++)
        { //从小到大枚举删除区间的长度
            bool flag = false;
            int j = len, k = len << 1; //两个观察点
            now = len;
            while (k <= n)
            {
                int LCP = lcp(j, k), LCS = lcs(j, k);
                if (LCP + LCS > len)
                {
                    for (int i = j - LCP + 1; i <= j - LCP + len; i++)
                        a[i] = len; //懒惰标记删除
                    now = LCP;      //一个细节:避免下一次的LCS与这次的LCP重合
                    flag = true;
                }
                else
                    now = len; //不会重合,则重置
                j += len;      //转移到下一个观察点
                k += len;
            }
            if (flag)
            {
                int cnt = 0;
                for (int i = 1; i <= n; i++)
                    if (a[i] != len)
                        s[++cnt] = s[i]; //重构字符串
                n = cnt;
                for (int i = 1; i <= n; i++)
                    w[i] = (w[i - 1] * 31 + s[i] - 'a') % mod; //重构哈希数组
            }
        }
        for (int i = 1; i <= n; i++)
            putchar(s[i]);
        return 0;
    }
    
  • 相关阅读:
    sql一对多的两个表的update
    textArea 高度自适应
    js切换不同的div的颜色
    [Python]闭包的理解和使用
    运维面试
    [linux]ubuntu修改镜像源
    [vsftpd] ubuntu14.04 ansible剧本安装vsftpd流程及报错排查
    windows环境下使用virtualenv对python进行多版本隔离
    [python]打印异常信息的不同方式
    [python]字典的直接赋值、浅拷贝和深拷贝解析
  • 原文地址:https://www.cnblogs.com/macesuted/p/solution-CF319D.html
Copyright © 2011-2022 走看看