「TAOI-2」Ciallo~(∠・ω )⌒★ 题解

news/2024/11/18 15:27:26/文章来源:https://www.cnblogs.com/SuporShoop/p/18435416

手玩了一个小时终于做出来了,这不得写一篇题解记录一下??

下面设 \(s\) 的长度为 \(n\)\(t\) 的长度为 \(m\)

考虑分类讨论:

如果 \(s\) 中有一个子串 \(s'\)\(t\) 完全相同(可以用哈希进行比较),设 \(s'\)\(s\) 的第 \(l\) 到第 \(r\) 个字符组成的字符串,则我们可以删除 \([1,l-1]\) 或者 \([r+1,n]\) 的某一个子区间,计算出它们的总个数就是 \(\dfrac{l\times (l-1)}{2}\)\(\dfrac{(n-r)\times (n-r+1)}{2}\),两者之和就是这一种情况的方案数。

我们也可以选定一个子串 \(s'\),满足 \(s'\) 的某一段前缀 \(pre\) 和某一段后缀 \(suc\) 不相交且两者连接到一起就是 \(t\)。这样的话我们删掉 \(pre\)\(suc\) 中间的字符就符合条件了。

例如 ciaohallo 要变成 ciallo 就可以选取前缀 cia 和后缀 llo,它们拼在一起就是我们要得到的字符串。值得注意的是,为了避免掉与上一种情况出现重复计算,我们需要满足 \(pre\)\(suc\) 中间必须留有至少一个字符。在上面的例子中,\(pre\)\(suc\) 之间就留有 oha 这一段字符。

但是怎么计算满足上述条件的 \(s'\) 的个数呢?我们可以设 \(p_i\) 表示 \(s\) 的第 \(i\) 个到第 \(n\) 个字符与 \(t\)最大公共前缀的长度,\(q_i\) 表示 \(s\) 的第 \(1\) 个到第 \(i\) 个字符与 \(t\)最大公共后缀的长度。对于 \(\forall i\in [1,n],j\in[i+m,n]\),如果有 \(p_i+q_j≥m\),则区间 \([i,j]\) 组成的子串 \(s'\) 就是满足条件的。当然 \(s'\) 变成 \(t\) 的删除方法也会有不同的代价。但是我们其实可以推出这个代价就是 \(p_i+q_j-m+1\)

为什么是对的?我们来看一下这张图:

图中第一行是 \([i,i+p_i-1]\) 的字符串 \(A\),第二行是 \([j-q_j+1,j]\) 的字符串 \(B\),我们有 \(p_i=6,q_j=4,m=7\),则 \(p_i+q_j≥m\),这个时候我们将它们进行对齐就得到了这张图。由于 \(p_i+q_j≥m\),所以 \(A\)\(B\) 一定有重合部分。由图可知重合部分的长度为 \(p_i+q_j-m=3\)

我们可以考虑 \(pre\) 的选择方法,进行得到唯一确定的 \(suc\) 的选择方法。我们的 \(pre\) 可以选择 ABAABAAABAABABAABA,即重合部分的长度加一 \(p_i+q_j-m+1\)。不难发现 \(pre\) 最长的时候就是取 \(A\),最短的时候就是取 \(A\) 中不与 \(B\) 重合的部分。这个结合图片应该是非常好理解的,大家可以自己思考一下。

综上所述,对于每一个 \(i\),我们找到所有的 \(j\in[i+m,n]\) 满足 \(p_i+q_j≥m\),给答案增加 \(p_i+q_j-m+1\)。当然我们还需要事先让 \(p_i,q_j\)\(m-1\) 取一个最小值,因为当 \(p_i\) 或者 \(q_j\) 等于 \(m\) 时,就会和第一种情况算重。

可是暴力找是 \(O(n^2)\) 的,我们需要优化。条件 \(p_i+q_j≥m\iff q_j≥m-p_i\),所以每一个 \(q_j\) 都是在 \([m-p_i,m-1]\) 内的,这样我们就不难想到权值线段树,储存区间范围内的 \(sum=\sum q_j\) 以及 \(q_j\) 的个数 \(num\)。则对于每一个 \(i\),它所带来的代价就是 \(sum+num\times (p_i-m+1)\)

然后就可以愉快地打出代码了:

#include<bits/stdc++.h>
#define Q(id,x,y,flag) query(1,1,id,x,y,flag)
using namespace std;
const int MAXN=4e5+5;
const unsigned long long base=179;
int n,m;
char a[MAXN],b[MAXN];
unsigned long long mul[MAXN],Hash1[MAXN],Hash2[MAXN];
int p[MAXN],q[MAXN];
struct node
{long long sum;int num;//sum表示和,num表示个数
}T[MAXN<<2];
void pushup(int x){ T[x].num=T[x<<1].num+T[x<<1|1].num,T[x].sum=T[x<<1].sum+T[x<<1|1].sum; }
void change_tree(int x,int l,int r,int k)
{if(l>r) return;if(l==r){T[x].sum+=l,T[x].num++;return;}int mid=(l+r)/2;if(k<=mid)  change_tree(x<<1,l,mid,k);else    change_tree(x<<1|1,mid+1,r,k);pushup(x);
}
long long query(int x,int l,int r,int L,int R,int flag)
{if(L>R) return 0;if(L<=l&&r<=R){if(!flag)   return T[x].sum;return T[x].num;}int mid=(l+r)/2;long long res=0;if(L<=mid)  res+=query(x<<1,l,mid,L,R,flag);if(R>mid)   res+=query(x<<1|1,mid+1,r,L,R,flag);return res;
}
long long solve(int x){ return 1ll*x*(x+1)/2; }
int main()
{cin>>(a+1)>>(b+1);n=strlen(a+1),m=strlen(b+1);mul[0]=1;for(int i=1;i<=n;i++)   Hash1[i]=Hash1[i-1]*base+a[i],mul[i]=mul[i-1]*base;//哈希预处理for(int i=1;i<=m;i++)   Hash2[i]=Hash2[i-1]*base+b[i];for(int i=1;i<=n;i++){if(a[i]!=b[1])  continue;int l=1,r=min(n-i+1,m);while(l<=r)//二分找最长公共前缀{int mid=(l+r)/2;if(Hash1[i+mid-1]-Hash1[i-1]*mul[mid]==Hash2[mid])  p[i]=mid,l=mid+1;else    r=mid-1;}p[i]=min(p[i],m-1);}for(int i=n;i>=1;i--){if(a[i]!=b[m])  continue;int l=1,r=min(i,m);while(l<=r)//同理找后缀{int mid=(l+r)/2;if(Hash1[i]-Hash1[i-mid]*mul[mid]==Hash2[m]-Hash2[m-mid]*mul[mid])  q[i]=mid,l=mid+1;else    r=mid-1;}q[i]=min(q[i],m-1);}long long res=0;for(int i=1;i<=n-m+1;i++){if(Hash1[i+m-1]-Hash1[i-1]*mul[m]==Hash2[m])    res+=solve(i-1)+solve(n-i-m+1);//第一种情况}if(q[n])	change_tree(1,1,m-1,q[n]);for(int i=n-m;i>=1;i--)//注意是逆序!!!!{if(p[i])	res+=Q(m-1,m-p[i],m-1,0)+Q(m-1,m-p[i],m-1,1)*(p[i]-m+1);if(q[i+m-1])	change_tree(1,1,m-1,q[i+m-1]);//每次要记得更新线段树}cout<<res;return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/804567.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

伯俊开发回忆录---VIP充值退款增加短信验证逻辑

一、前提总部财务需要增加对VIP卡充值退款的管控,防止资金被异常盗用, 1、针对VIP充值退款获取验证码,表单增加验证码字段 2、系统随机生成6位数验证码并生成提醒信息通过公司发送平台进行发送 三、校验规则未输入验证码不允许提交 验证码校验不通过提示重新输入

我的博客生涯开始了

我的博客生涯开始了

渗透测试入门

什么是渗透测试? 定义: 渗透测试完全模拟黑客可能使用的攻击技术和漏洞发现技术,对目标系统的安全做深入的探测,发现系统最脆弱的环节,以期发现和挖掘系统中存在的漏洞,然后输出渗透测试报告,并提交给网络所有者。网络所有者根据渗透人员提供的渗透测试报告,可以清晰知…

常间的css样式问题处理

flex导致文字省略失效 单独使用文字省略,按预期工作: 给元素加上flex,文字省略失效: 解决方案:flex和文字省略不要放到一个元素上。 flex布局中,文字溢出省略不生效的问题 问题展示.container {display: flex;width: 400px;border: 1px solid #000; }.content {flex: 1; …

Spring上传文件乱码问题(问号版)

Spring上传文件乱码问题(问号版) 目录Spring上传文件乱码问题(问号版)一、问题描述:二、原因分析三、解决办法 一、问题描述: spring项目上传文件,后端接收文件并获取文件名称,名称中文变成 “?”,例如:??abc()??.xml,其中问号为中文字符 // 前端传递参数 Mult…

伯俊开发回忆录---云POS待办事项增加稽核通知功能

一、事件前景总部财务稽核通知下发流程: 1.整理EXECL通知督导, 2.督导通知对应的门店, 3.收集完反馈意见汇报给分区财务审核 4.分区财务审核之后再通知总部财务审核, 这样整个稽核流程以及周期将大大影响稽核效率,因此希望在云POS门店端直接增加待办事项减少中间沟通环节。…

我,一个小白,居然用 AI 工具修改了公司前端代码!

背景 有一天同事发现公司网站的某个页面上有三个 H1 标签,懂行的都知道,有三个 H1 标签虽然不会对网站的访问产生影响,但是对于搜索引擎来讲,就比较麻烦了,因为一般搜索引擎都是靠 H1 标签、TDK 等来对网页的内容进行抓取,然后再进行质量优劣的判断。三个 H1 标签,搜索引…

Docker打包Net8.0镜像

Docker 常用命令 Docker 是一种用于构建、打包和运行应用程序的容器化工具,以下是一些常用的 Docker 命令及其说明: 1. Docker 基础命令 docker version # 查看 Docker 的版本信息 docker info # 查看 Docker 系统信息 docker build -t <image_name> . #构建镜像 docke…

利用Python开发Exporter,集成Prometheus和Grafana对进程监控

利用Python开发Exporter,集成Prometheus和Grafana对进程监控 在现代软件开发和运维中,监控是确保系统稳定运行和快速响应问题的重要手段。Prometheus和Grafana的组合是监控领域的强大工具,它们能够收集、处理和展示各种指标数据。本文将介绍如何利用Python开发一个Exporter,…

软工结对项目

这个作业属于哪个课程 结对项目这个作业要求在哪里 结对项目这个作业的目标 合作完成一个自动生成小学四则运算题目的命令行程序结对组合成员介绍结对组合成员姓名 学号 GitHub项目地址苏清仪 3222004337 GitHub项目地址张易欣 3222004811 GitHub项目地址PSP表格PSP2.1 Persona…

Docker镜像、Spark支持多表...Apache SeaTunnel 2.3.8版本将带来的惊喜

Apache SeaTunnel 2.3.8版本即将于大家见面,近日,Apache SeaTunnel PMC Member 范佳在社区的交流会上为大家提前透露了关于这个新版本即将进行的功能与特性更新概况,详细内容如下: SeaTunnel 简介 SeaTunnel是一个高性能的开源分布式数据集成系统,支持各种数据源的实时流式…

校青协宣传推广中心-二面-作品展示

主题图片ps:主题图片引用自扬州大学青年志愿者协会官Q 作品展示