把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷5826】【模板】子序列自动机

点此看题面

  • 给定一个长度为\(n\)的文本串,字符集大小为\(m\)
  • \(q\)次询问,每次给定一个字符串,询问是否为文本串的一个子序列。
  • \(n,m,q\le10^5\)

思想核心:贪心

其实就这题而论,子序列自动机是一个非常简单易懂的算法,闲得无聊来水一水。

核心思想是贪心,就是考虑我们每次找到尽可能近的字符匹配一定是最优的。

方法一:主席树

由于常见字符串问题的字符集大小是\(26\),所以相信大家都写过一个东西,就是记录\(S_{i,c}\)表示第\(i\)位之后第一个字符\(c\)所在位置。

要维护这个只需从后往前枚举,每次\(S_i\)继承\(S_{i+1}\),并更新\(S_{i,a_{i+1}}=i+1\)

这题字符集很大,考虑继承操作就相当于是一个版本的赋值,随后我们进行的是在新版本上的一个单点修改。

发现这就是一个用主席树实现可持久化数组的过程,非常板子。

方法二:\(vector\)+二分

直接对于每个字符集开一个\(vector\),要找到第\(i\)位之后第一个字符\(c\),只要在字符\(c\)对应的\(vector\)中求出\(upper\_bound(i)\)即可。

而且把\(vector\)换成\(set\)甚至能实现带修,这是主席树做不到的。所以说主席树直接被全方位吊打。

代码:\(O(nlogn)\)

当然是方法二啦,不会真的有人会去写主席树吧。

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
using namespace std;
int n,m;vector<int> S[N+5];vector<int>::iterator it;
int main()
{
	RI Qt,i,x,y;for(scanf("%d%d%d%d",&x,&n,&Qt,&m),i=1;i<=n;++i) scanf("%d",&x),S[x].emplace_back(i);//对每种字符维护个vector
	RI t;W(Qt--) {for(scanf("%d",&t),x=0;t;--t) scanf("%d",&y),
		~x&&(x=(it=upper_bound(S[y].begin(),S[y].end(),x))==S[y].end()?-1:*it);puts(~x?"Yes":"No");}//利用upper_bound查找下一位
	return 0;
}
posted @ 2021-05-18 08:59  TheLostWeak  阅读(56)  评论(0编辑  收藏  举报