首页 > 其他分享 >2021 ICPC EC Final B. Beautiful String 题解

2021 ICPC EC Final B. Beautiful String 题解

时间:2022-10-30 21:45:58浏览次数:64  
标签:Beautiful String lcp int 题解 sum 枚举 tt 前缀

2021 ICPC EC Final B. Beautiful String 题解

题意

问给定字符串t的所有子串中形如"114514"分割方案之和。其中'1'、'4'、'5'表示某一字符串,且可重复。

分析(暴力\(n^3\))

lcp[i][j]表示后缀ij的最长公共前缀,那么如果lcp[i][j] >= j - i则说明ij这一段字符串在j之后重复出现了。可以使用\(n^2\)的dp预处理:lcp[i][j]= s[i]==s[j] ? lcp[i + 1][j + 1] + 1 : 0

在\(n^2\)枚举ij判断出lcp[i][j] >= j - i之后,枚举k,若lcp[j][k]j - i大,则答案需要加上这一部分lcp[j][k]减掉j - i的长度,当然为了保证绿色部分至少存在1个,k要从j + 3开始枚举,而且要保证lcp[j][k]不能超出了k - j - 1

代码(TLE)

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

const int N = 5005;

char s[N];

int main() {
    int tt;
    scanf("%d",&tt);
    while(tt--) {
        scanf("%s",s+1);
        int n = strlen(s + 1);
        vector<vector<int>> lcp(n + 2, vector<int>(n + 2));
        for (int i = n; i >= 1; i--)
            for (int j = n; j >= i; j--)
                if (s[i] == s[j]) lcp[i][j] = lcp[i + 1][j + 1] + 1;
        long long ans = 0;
        for (int i = 1; i <= n; i++)
            for (int j = i + 1; j <= n; j++)
                if (lcp[i][j] >= j - i) {
                    for (int k = j + 3; k <= n; k++) {
                        if (lcp[j][k] > j - i) {
                            int mi = min(lcp[j][k], k - j - 1) - (j - i);
                            ans += max(mi, 0);
                        }
                    }
                }
        printf("%lld\n",ans);
    }
}

分析(预处理\(n^2\))

对于上述过程,i、j、k是同时枚举的,std的优化是先枚举i、j,将j对后缀的贡献记录下来,再枚举j、k

对于一个合法的i、jlcp[i][j] >= j - i,若存在k对答案有贡献,那么lcp[j][k]也至少要是j - i,换句话说,从j往后j - i到整个字符串末尾都有可能会产生贡献。

sum[j][len]表示从j开始长度为len的贡献,那么若lcp[i][j] >= j - isum[j][j - i]、sum[j][j - i + 1]、...、sum[j][length(s) - 1]、sum[j][length(s)]都要+1,当之后枚举j、k的时候如果满足条件了,我们应该从j - i开始加到长度min(lcp[j][k], k - j - 1) - 1这一段的sum值,也就是sum[j][j - i] + sum[j][j - i + 1] + ... + sum[j][min(lcp[j][k], k - j - 1) - 2]、sum[j][min(lcp[j][k], k - j - 1) - 1]

对上述一些操作进行优化:

"sum[j][j - i]、sum[j][j - i + 1]、...、sum[j][length(s) - 1]、sum[j][length(s)]都要+1"

其实是对一段后缀同时加,可以使用差分优化,即sum[j][j - i] + 1、sum[j][length(s) + 1] - 1,只不过后续根本用不到sum[j][length(s) + 1],所以只需要对sum[j][j - i] + 1,然后求前缀和即可。

"枚举j、k的时候,我们应该从j开始加上长度min(lcp[j][k], k - j - 1) - 1这一段的sum值"

其实是求一段前缀和,而求前缀和的原始数组就是上面差分数组计算出来的数组。

所以只要在枚举i、j的时候只统计差分数组的贡献,然后求两遍前缀和,就得到了sum数组的前缀和,这里不妨直接将sum先作为差分数组使用,在其的基础上做两遍前缀和将其转化为前缀和数组即可。这样就能够在枚举j、k的时候\(O(1)\)计算贡献了。

由于要复制一份,满足条件的贡献至多不超过原字符串长度的一半,因此sum第二维开到一半就够了。

代码

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

const int N = 5005;

char s[N];

int main() {
    int tt;
    scanf("%d",&tt);
    while(tt--) {
        scanf("%s",s+1);
        int n = strlen(s + 1);
        vector<vector<int>> lcp(n + 2, vector<int>(n + 2));
        for (int i = n; i >= 1; i--)
            for (int j = n; j >= i; j--)
                if (s[i] == s[j]) lcp[i][j] = lcp[i + 1][j + 1] + 1;
        long long ans = 0;
        vector<vector<int>> sum(n + 1, vector<int>(n / 2 + 2));
        for (int i = 1; i < n; i++)
            for (int j = i + 1; j <= n; j++)
                if (lcp[i][j] >= j - i)
                    sum[j][j - i]++;
        for (int i = 1; i <= n; i++) {
          for (int j = 1; j <= n / 2 + 1; j++)
            sum[i][j] += sum[i][j - 1];
          for (int j = 1; j <= n / 2 + 1; j++)
            sum[i][j] += sum[i][j - 1];
        }
        for (int j = 1; j < n; j++) {
          for (int k = j + 1; k <= n; k++) {
            int len = min(lcp[j][k], k - j - 1) - 1;
            if (len >= 0) ans += sum[j][len];
          }
        }
        printf("%lld\n",ans);
    }
}

标签:Beautiful,String,lcp,int,题解,sum,枚举,tt,前缀
From: https://www.cnblogs.com/KIMYON/p/16842332.html

相关文章

  • typescript number 转 string(转)
    转自:number转string一、双点解析10..toString();二、括号先计算再转换(10).toString();三、加空串10+''转自:number转string......
  • acm22纳新题题解:D.喜子哥开空调
    喜子哥开空调Description喜子掌管着工作室的空调遥控器,他可以自由调整室内的温度,(这么牛?!我咋不信。。)但每个人最喜欢的室内温度不都相同,如果当前室温k与自身适应的......
  • CSP-S 2022 Unofficial 题解
    去年有个CSP-S2021Unofficial的题解但是那玩意咕掉了(主要是不想写最后一题,但是准备省选的时候会补上毕竟ZJ卷怪一堆懂得都懂),不过今年保证在NOIP2022前会写完这份题......
  • Java中String的分词方法split的使用
    在java.lang包中有String.split()方法,返回是一个数组1、如果用“.”作为分隔的话,必须是如下写法:String.split("\\."),这样才能正确的分隔开,不能用String.split(".");2、......
  • 583.delete-operation-for-two-strings 两个字符串的删除操作
    问题描述583.两个字符串的删除操作解题思路dp[i][j]表示对word1的前i个字符,word2的前j个字符,使得它们相同的最小步数:if(word1[i-1]==word2[j-1]),dp[i][j]=......
  • String、StringBuffer、StringBuilder的区别
    ①String的对象不可变,StringBuilder和StringBuffer的对象可变。②String、StringBuffer线程安全,StringBuilder线程不安全。③StringBuilder速度最快,StringBuffer次之,Stri......
  • CSP2022-J组题解
    最后一次j组了,写篇题解纪念一下A假如\(a=1\),\(a^b=1\)假如\(a>1\),可以发现当\(b>30\)时\(a^b\)必然大于\(10^9\)于是我们可以暴力计算,如果计算的过程中大于\(1......
  • CF1622D. Shuffle 题解 组合数学/枚举
    题目链接:​​https://codeforces.com/problemset/problem/1622/D​​题目大意:给定一个长度为\(n\)的01字符串\(s\),你可以对这个字符串进行最多一次操作,该次操作需要选择......
  • Java 使用StringBuilder组装字符串
    下面这个例子来自SpringBoot源码,这里是要打印程序启动的时间这样的字符串,需要拼装的信息有程序名字,启动时长,JVM时长。privateStringBuildergetStartedMessage(StopWatc......
  • WIN2012远程桌面授权服务器许可证问题解决方法
    WIN2012服务器报错为由于没有远程桌面授权服务器可以提供许可证,远程会话连接已断开。请跟服务器管理员联系。原因是服务器安装了远程桌面服务RemoteApp,这个是需要授权的。微......