首页 > 其他分享 >题解:CF1975G Zimpha Fan Club

题解:CF1975G Zimpha Fan Club

时间:2024-05-27 09:00:33浏览次数:33  
标签:dots 匹配 Zimpha Club 题解 复杂度 Poly int 字符串

\(\text{Link}\)

题意

给你两个长度分别为 \(n,m\) 的字符串 \(s,t\),其中仅包含小写字母、-*,你需要将 - 替换为任意小写字母,* 替换为任意小写字母构成的字符串(可以为空串),问是否可以使得 \(s'=t'\)。

\(n,m\le 2\times 10^6\),12s。

思路

首先我们有工具:NTT 优化带通配符的字符串匹配。对于两个不含 * 的字符串 \(s,t\),我们可以求出 \(s\) 能匹配 \(t\) 中的哪些子串。具体地,构造序列 \(a,b\),如果 \(s_i\) 是通配符则 \(a_i=0\),否则 \(a_i\) 表示 \(s_i\) 是第几个字母,\(b_i\) 同理。

那么如果 \(s\) 能匹配 \(t_{i,\dots,i+m-1}\),当且仅当

\[f_i=\sum_{j=0}^{m-1}(t_{i+j}-s_j)^2t_{i+j}s_j=0 \]

把括号拆掉后三次差卷积可以计算。


回到原问题,我们发现 * 比较难处理。

经过手玩,\(s,t\) 同时都有 * 的情况是简单的:令 \(p\) 为最小的数使得 \(s_p\) 或 \(t_p\) 为 *,则 \(s_{0,\dots,p-1}\) 要匹配 \(t_{0,\dots,p-1}\),后缀同理。而 \(s,t\) 同时不含 * 的情况更为平凡,直接逐位判断即可。

于是经过平凡的操作,我们将问题转化成了 \(s\) 形如 \(*s_1*s_2*\cdots*s_k*\),而 \(t\) 不含 * 的形式。于是就有贪心:\(s_1\) 匹配 \(t\) 中第一个能匹配的位置,并将其匹配位置及其前所有字符都删去,按同样的方式依次匹配 \(s_{2\dots k}\),如果全部成功匹配则有解,否则无解。

但如果每次直接将 \(s_i\) 与 \(t\) 做 NTT 字符串匹配复杂度不可接受,我们发现时间复杂度的浪费主要为我们只需要找到 \(s_i\) 在 \(t\) 中第一个匹配的位置,但我们把所有位置是否匹配都求出来了。

如果字符串无通配符,则我们可以在 \(O(1)\) 时间内判断 \(s_i\) 与 \(t\) 中以 \(p\) 开头的等长字符串是否匹配,所以我们可以从左向右依次判断,以 \(O(1)\) 的代价删去一个字符。而带上通配符,我们可以在 \(O((r-l+|s_i|)\log (r-l+|s_i|))\) 的时间复杂度内内判断 \(s_i\) 与 \(t\) 中以 \([l,r]\) 开头的等长字符串是否匹配,据此,我们可以想到优化的思路:在 \(l\) 相关的时间复杂度内删去 \(l\) 个字符。

我们每次把 \(s_i\) 与 \(t\) 的前 \(2|s_i|\) 个字符进行匹配,在 \(p\) 位置匹配成功则把 \(t_{p+|s_i|-1}\) 及以前的字符全部删除,否则删除 \(t\) 的前 \(l\) 个字符。容易发现这符合我们的需求——在 \(O(|s_i|\log |s_i|)\) 的时间复杂度内删去了不少于 \(|s_i|\) 个字符。

时间复杂度 \(O(n\log n)\)。

核心代码:

inline Poly MulR(const Poly &a,const Poly &b){
	int n=a.size(),m=b.size();
	Poly F=a,G=b;
	reverse(G.begin(),G.end());
	F=F*G;
	Poly H;
	for(int i=0;i<n-m+1;i++)
		H.push_back(F[i+m-1]);
	return H;
}
int n,m;
short s[M],t[M];
Poly F1,G1,F2,G2,F3,G3,H;
inline int check(int l1,int r1,int l2,int r2){
	F1.clear(),F2.clear(),F3.clear();
	for(int i=l2;i<=r2;i++){
		int v=t[i];
		F1.push_back(v),F2.push_back(v*v),F3.push_back(v*v*v);
	}
	G1.clear(),G2.clear(),G3.clear();
	for(int i=l1;i<=r1;i++){
		int v=s[i];
		G1.push_back(v),G2.push_back(v*v),G3.push_back(v*v*v);
	}
	H=MulR(F3,G1)+MulR(F1,G3)-MulR(F2,G2)*2;
	for(int i=0;i<H.size();i++)
		if(!H[i])
			return l2+i;
	return -1;
}
int main(){
//	freopen("G.in","r",stdin);
	n=read(),m=read();
	bool fls=0,flt=0;
	for(int i=1;i<=n;i++){
		char c=getc();
		if(c=='*') fls=1,s[i]=-1;
		else s[i]=c=='-'?0:c-'a'+1;
	}
	for(int i=1;i<=m;i++){
		char c=getc();
		if(c=='*') flt=1,t[i]=-1;
		else t[i]=c=='-'?0:c-'a'+1;
	}
	if(!fls&&!flt){
		bool fl=n==m;
		for(int i=1;i<=n;i++)
			fl&=!s[i]||!t[i]||s[i]==t[i];
		return puts(fl?"Yes":"No"),0;
	}
	if(fls&&flt){
		for(int i=1;;i++)
			if(s[i]==-1||t[i]==-1) break;
			else if(s[i]&&t[i]&&s[i]!=t[i]) return puts("No"),0;
		for(int i=n,j=m;;i--,j--)
			if(s[i]==-1||t[j]==-1) break;
			else if(s[i]&&t[j]&&s[i]!=t[j]) return puts("No"),0;
		return puts("Yes"),0;
	}
	if(!fls) swap(n,m),swap(s,t);
	Prefix(n+m);
	int sts=0,stt=0,eds=0,edt=0;
	for(int i=1;;i++)
		if(s[i]==-1||t[i]==-1){ sts=stt=i;break; }
		else if(s[i]&&t[i]&&s[i]!=t[i]) return puts("No"),0;
	for(int i=n,j=m;;i--,j--)
		if(s[i]==-1||t[j]==-1){ eds=i,edt=j;break; }
		else if(s[i]&&t[j]&&s[i]!=t[j]) return puts("No"),0;
	while(s[sts]==-1) sts++;
	for(int i=sts,j=stt;i<=eds;){
		int r=i;
		while(r+1<=n&&s[r+1]!=-1) r++;
		int l=r-i+1;
		while(1){
			if(j>edt) return puts("No"),0;
			int jr=min(edt,j+l*2);
			int tmp=check(i,r,j,jr);
			if(tmp==-1) j+=l;
			else{ j=tmp+l;break; }
		}
		while(r+1<=n&&s[r+1]==-1) r++;
		i=r+1;
	}
	puts("Yes");
}

标签:dots,匹配,Zimpha,Club,题解,复杂度,Poly,int,字符串
From: https://www.cnblogs.com/cyffff/p/18214750

相关文章

  • 2024 CCPC 全国邀请赛(山东)暨山东省大学生程序设计竞赛题解 A C F I K
    超时就是AC队第一次打ccpc比较菜蒟蒻只能做五题ProblemA.打印机算法:二分思路:二分时间每次check查看当前时间内所有打印机可以打印的个数是否符合条件注意二分的右边界为2e18ProblemC.多彩的线段2算法:组合数思路:将所有线段按照起点从左到右排序枚举线段每次将当......
  • Codeforces Round #947 题解 (A~G)
    目录A.BazokaandMocha'sArrayB.378QAQandMocha'sArrayC.ChamoandMocha'sArrayD.PainttheTreeE.ChainQueriesF.SetG.ZimphaFanClubA.BazokaandMocha'sArray枚举每个循环移位判断.B.378QAQandMocha'sArray首先最小的数肯定得选,删掉最小的数的倍数......
  • 【leetcode 399 周赛】【题解】
    第一题和第三题一样。就是求约数第二题就是模拟第4题使用线段树1,3题代码实际上发现没有下面代码的负载,比如:a*b=n,枚举a就好,a在[1,sqrt(n)内。importjava.util.*;classSolution{publicintnumberOfPairs(int[]nums1,int[]nums2,intk){......
  • CATIA入门操作——萌新宝宝遇到的奇奇怪怪的问题解决,持续更新中。。。
    目录引出发生肾么事了??鼠标中键旋转不了解决:特征树不显示参数关系我的窗口去哪了?插曲:草图工具的调出插曲:颜色工具栏显示弹窗警告警告:创建约束是临时的操作技巧技巧:快速隐藏不相关元素工具栏怎么变成水平?总结异形弹簧新建几何体草图编辑,画一条样条线进行扫掠,圆心和半......
  • CF1821F Timber 题解
    题意:在\([1,n]\)的区间里放\(m\)棵树,每棵树的高度为\(k\)。求有多少种放置树的方法,满足:每个树都在整点上,且每个点最多只能放一棵树。存在一种砍倒树的方案,使得树倒了之后不会越界,也不会有某个点被超过一棵树占据。你可以自由选择树向左倒(也就是占据区间\([x-k,x]\))......
  • [SHOI2007]书柜的尺寸 题解
    题目链接设\(f_{i,t1,t2}\)表示前\(i\)本书,第一层的宽度为\(t1\),第二层的宽度为\(t2\),所需要的最小高度。第三层宽度\(t3=\sum_{i=1}^{i}t_i-t1-t2\)。但本题还有一个重要限制:每层的高度取决于该层最高的书,如果直接按照顺序加入书本,从\(dp\)的状态来看,无法确定新书本......
  • CCF-GESP 等级考试 2024年3月认证C++一级真题解析
    2024年03月真题1单选题第1题C++表达式(3-2)*3+5的值是()。A.-13B.8C.2D.0正确答案:B.8解析:首先计算括号中的表达式(3-2),得到(1)。接下来进行乘法运算(1*3),得到(3)。最后进行加法运算(3+5),得到(8)。因此,表达式的值是(8)。第2题C++......
  • 题解:P6880 [JOI 2020 Final] オリンピックバス
    一个比较重要的性质:反转的边要在最短路上才会有贡献。我们可以先跑一遍最短路,记录下整颗最短路树,然后暴力的对每一条边进行判断,反转。我们建正反图各两个,分别以\(1\),\(n\)为起点。\(n\),\(1\)为终点。然后对每个图跑最短路,记录下最短路树。若不反转任何一条边,则答案为\(1\)......
  • 题解:CF1119D Frets On Fire
    大水题。首先,若区间内只有一根弦,不会对答案有贡献。我们思考如何对答案产生贡献。我们知道,对于每一个\(s_i\),都会产生一段\(s_i+r-l\)的连续序列,在对\(s\)数组排序后,若每个\(s_i+r-l\ges_{i+1}\)则答案为\(s_n+r-(s_1+l)+1\)。若够不到下一位呢?我们在\(s_n+r-(s_1+l......
  • UVA11922 Permutation Transformer 题解
    题目传送门前置知识无旋treap解法与luoguP3391【模板】文艺平衡树不同的是本题翻转后需要放到整个序列的末尾。由于需要翻转后放到末尾,故无旋Treap在维护文艺平衡树的过程中合并时跳着合并即可。代码#include<bits/stdc++.h>usingnamespacestd;#definelllong......