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

news/2024/9/27 12:17:59

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

下面设 \(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.ryyt.cn/news/65360.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

伯俊开发回忆录---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…