首页 > 其他分享 >SGU 505

SGU 505

时间:2024-07-10 22:42:36浏览次数:6  
标签:cur int SGU 后缀 ra using 505 前缀

link

这里讲两种做法,一个在线,一个离线。

在线

我们分别考虑前缀和后缀。有一个比较重要的结论,就是把 \(s\) 按照字典序排序以后,相同前缀的出现位置(其实就是 rank)是连续的。\(s\) 翻过来,相同后缀的也是连续的。

这样我们就可以求出每一个询问前缀和后缀对应的区间是什么,然后就要求区间重合的数有多少个就可以了。一个做法是,设两个排完序的 \(id\) 为 \(p,q\),我们把 \(p\) 弄成在 \(q\) 中出现的位置,这样就要求 \(p\) 区间内在一个范围内数的个数。

现在的问题是给定一个数组 \(a\),询问 \(a_{l\sim r}\) 中 \(c\le a_i\le d\) 的个数,也就是小于 \(d\) 的个数减去小于 \(c-1\) 的个数。可以用主席树维护。(也可以分块,但是这样就是 \(\mathcal{O}(n\sqrt{n}\log n)\) 的了,不知道能不能过)

还有一个可能的问题是查找前缀/后缀对应区间。这个用二分有可能特判比较多(端点的特判是不是这个前缀/后缀),所以我们可以直接用一个 trie,每一个节点记录对应到这个点的区间。

可能要卡一点空间。代码不算难写。

#include <bits/stdc++.h>

using namespace std;

using ll = long long;

const int N = 1e5+5;

int n,val[N],idx[N];

struct node {
	string s;
	int id;
	bool operator < (const node &a) const{
		return s<a.s;
	}
} a[N],ra[N];

string s[N],rs[N];

int rt[N],cnt;

struct tnode {
	int l,r,sum;
} t[N*20];

void upd(int l,int r,int &x,int y,int pos){
	t[++cnt]=t[y];
	t[cnt].sum++;
	x=cnt;
	if (l==r){
		return;
	}
	int mid=l+r>>1;
	if (pos<=mid){
		upd(l,mid,t[x].l,t[y].l,pos);
	}
	else{
		upd(mid+1,r,t[x].r,t[y].r,pos);
	}
}

int qy(int l,int r,int x,int y,int k){
	if (k==0){
		return 0;
	}
	if (r<=k){
		return t[y].sum-t[x].sum;
	}
	int mid=l+r>>1;
	if (k<=mid){
		return qy(l,mid,t[x].l,t[y].l,k);
	}
	else{
		return t[t[y].l].sum-t[t[x].l].sum+qy(mid+1,r,t[x].r,t[y].r,k);
	}
}

struct trie {
	int ch[N*10][26],mn[N*10],mx[N*10],tot=1;
	void init(){
		for (int i=0; i<N*10; i++){
			mn[i]=1e9;
			mx[i]=-1e9;
		}
	}
	void ins(string s,int id){
		int cur=1;
		for (auto c : s){
			if (!ch[cur][c-'a']){
				ch[cur][c-'a']=++tot;
			}
			mn[cur]=min(mn[cur],id);
			mx[cur]=max(mx[cur],id);
			cur=ch[cur][c-'a'];
		}
		mn[cur]=min(mn[cur],id);
		mx[cur]=max(mx[cur],id);
	}
	pair<int,int> seg(string s){
		int cur=1;
		for (auto c : s){
			if (!ch[cur][c-'a']){
				return {-1,-1};
			}
			cur=ch[cur][c-'a'];
		}
		return {mn[cur],mx[cur]};
	}
} ta,tr;

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);

	cin>>n;
	for (int i=1; i<=n; i++){
		cin>>a[i].s;
		a[i].id=i;
		ra[i].s=a[i].s;
		reverse(ra[i].s.begin(),ra[i].s.end());
		ra[i].id=i;
	}
	sort(a+1,a+1+n);
	sort(ra+1,ra+1+n);
	for (int i=1; i<=n; i++){
		s[i]=a[i].s;
		rs[i]=ra[i].s;
		idx[ra[i].id]=i;
	}
	for (int i=1; i<=n; i++){
		val[i]=idx[a[i].id];
	}
	for (int i=1; i<=n; i++){
		upd(1,n,rt[i],rt[i-1],val[i]);
	}
	ta.init();
	tr.init();
	for (int i=1; i<=n; i++){
		ta.ins(s[i],i);
		tr.ins(rs[i],i);
	}
	int q;
	cin>>q;
	while (q--){
		string x,y;
		cin>>x>>y;
		reverse(y.begin(),y.end());
		auto u=ta.seg(x);
		auto v=tr.seg(y);
		if (u.first==-1 || v.first==-1){
			cout<<"0\n";
			continue;
		}
		int ans=qy(1,n,rt[u.first-1],rt[u.second],v.second);
		ans-=qy(1,n,rt[u.first-1],rt[u.second],v.first-1);
		cout<<ans<<"\n";
	}
	return 0;
}

离线

这个是用 ACAM 的做法。首先复习一下 ACAM 能做什么:求一个文本串里面出现其他串的个数,多次询问。

如果我们把一个串写成 \(s_i|s_i\) 的形式(如 ab 写成 ab|ab),然后查询相当于问有没有一个 \(suf|pre\) 的子串。我们把所有的文本串用 ,(或其他字符)连成一个长串,就可以直接查询了!

这个除了模板的程序会更好写,但是唯一劣势是离线。

Credit: Codeforces @cayaxi09.

#include <bits/stdc++.h>

using namespace std;

using ll = long long;

const int N = 5e5+5;

int n;
string T,s[N];
int ch[N][30],tag[N],fa[N],ans[N];
int cnt,vis[N],in[N],mp[N];

void init(){
	cnt=1;
	for (int i=0; i<N; i++){
		memset(ch[i],0,sizeof ch[i]);
		tag[i]=fa[i]=0;
	}
	for (int i=0; i<N; i++){
		vis[i]=0;
	}
}

int get(char c){
	if ('a'<=c && c<='z'){
		return c-'a';
	} 
	if (c=='|'){
		return 26;
	}
	return 27;
}

void ins(string s,int id){
	int cur=1;
	for (int i=0; i<s.size(); i++){
		int c=get(s[i]);
		if (!ch[cur][c]){
			ch[cur][c]=++cnt;
		}
		cur=ch[cur][c];
	}
	if (!tag[cur]){
		tag[cur]=id;
	}
	mp[id]=tag[cur];
}

void get_fail(){
	queue<int> q;
	q.push(1);
	for (int i=0; i<28; i++){
		ch[0][i]=1;
	}
	fa[1]=0;
	while (!q.empty()){
		int u=q.front();
		q.pop();
		for (int i=0; i<28; i++){
			if (!ch[u][i]){
				ch[u][i]=ch[fa[u]][i];
			}
			else{
				fa[ch[u][i]]=ch[fa[u]][i];
				in[fa[ch[u][i]]]++;
				q.push(ch[u][i]);
			}
		}
	}
}

void qy(string s){
	int cur=1;
	for (int i=0; i<s.size(); i++){
		int c=get(s[i]);
		int z=ch[cur][c];
		ans[z]++;
		cur=z;
	}
}

void sol(){
	queue<int> q;
	for (int i=1; i<=cnt; i++){
		if (!in[i]){
			q.push(i);
		}
	}
	while (!q.empty()){
		int u=q.front();
		q.pop();
		vis[tag[u]]=ans[u];
		in[fa[u]]--;
		ans[fa[u]]+=ans[u];
		if (!in[fa[u]]){
			q.push(fa[u]);
		}
	}
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);

	cin>>n;
	init();
	for (int i=1; i<=n; i++){
		string t;
		cin>>t;
		T+=t;
		T+="|";
		T+=t;
		T+=",";
	}
	int q;
	cin>>q;
	for (int i=1; i<=q; i++){
		string x,y;
		cin>>x>>y;
		s[i]=y+"|"+x;
		ins(s[i],i);
	}
	get_fail();
	qy(T);
	sol();
	for (int i=1; i<=q; i++){
		cout<<vis[mp[i]]<<"\n";
	}
	return 0;
}

标签:cur,int,SGU,后缀,ra,using,505,前缀
From: https://www.cnblogs.com/SFlyer/p/18295142

相关文章

  • 实验一百三十八:64位 WS2812B8*8 xRGB 5050 LED模块 ws2812s像素点阵屏
    另外:https://blog.csdn.net/asdhnkhn/article/details/133206543  【Arduino】168种传感器模块系列实验(资料代码+仿真编程+图形编程)实验一百三十八:64位WS2812B8*8xRGB5050LED模块ws2812s像素点阵屏知识点:WS2812B是一个集控制电路与发光电路于一体的智能外控LED光源......
  • CF/505/D 题解
    思路这道题的思路其实是根据样例图片来的。首先第一张:这张图片可以得知$n$个点没有环的时候最少需要$n-1$个点。再看第二张:这个图确实很难思考,但稍微思考一下如果我们把含有一个强连通分量的图变成一整个强连通图。这个图的边数不就变成$n$了吗?来推一下......
  • Easy-laser激光对中仪维修D505激光测平仪维修
    Easylaser激光对中仪多应用于风力发电业的塔架、机架、轮毂、偏航轴承和变桨轴承的几何指标测量中。此系列常见维修型号包括D450;D480;D505;D525;D550等。Easy-Laser对中仪维修注意事项:测量功能包括:塔架法兰平面度、内倾度。机架发电机底座、主轴轴承底座和齿轮箱底座平面度。......
  • 各版本操作系统对.NET支持情况(0505更新)
    https://www.newlifex.com/tech/os_net 借助虚拟机和测试机,检测各版本操作系统对.NET的支持情况。安装操作系统后,实测安装相应运行时并能够运行星尘代理为通过。 测试平台:VMwareWorkstation镜像来源:MSDNITellYou参考:.NETFramework版本和依赖关系.NETFramework......
  • [20250511]建立完善s2h.sql脚本.txt
    [20250511]建立完善s2h.sql脚本.txt--//写过一个sql_id计算hash_value的bashshell脚本,实际上oracle使用dbms_utility.SQLID_TO_SQLHASH就可以实现,$cats2h.sql--Copyright2023lfree.Allrightsreserved.--LicensedundertheApacheLicense,Version2.0.SeeLICENSE......
  • 0505一般质疑
    一般质疑底层逻辑质疑方式无论据有结论A和非A的矛盾有理由的得出相反的结论有论据有结论--近似看成A=》B-和A且非B矛盾绝大多数肯定论据得出相反结论--得不出该结论......
  • CHC5054Web应用程序开发
    Web应用程序开发:课件分配这门课程相当于CHC5054模块100%的分数。您还需要以下模块的技能:●CHC4008(Python编程)●CHC4007(设计报告)●CHC5049(数据库设计)●CHC5226(安全实施)规格您的任务是开发和测试一个简单的基于网络的学习管理系统的完整堆栈,该系统旨在促进教育课程的管理、交付和跟......
  • 五 505. 火柴排队 (归并排序|离散化)
    五505.火柴排队(归并排序|离散化)思路:先将两组数组按(2314->2013;3214->2103)规则排序,然后使用c数组建立双方的映射,从c[ai[i]]=bi[i],接着就是使用归并排序求c数组的逆序对即交换次数。importjava.util.*;publicclassMain{privatestaticint......
  • 「CF505C」 Mr. Kitayuta, the Treasure Hunter
    题意在数轴上有\(n\)块宝石,当你走到一个点时,可以获得点上所有的宝石。若前一步走了\(c\)个单位长度,那么下一步可以走\(c-1,c,c+1\)个单位长度。你最开始在原点,可以向右走\(d\)个单位长度,求最多可获得多少宝石。分析设\(f_{i,j}\)表示在第\(i\)个点,上一步走\(j\)......
  • SGU171 Sarov zones 题解
    题意:有\(K\)个城市,第\(i\)城市至多有\(N[i]\)个人,每个城市有一个属性\(Q[i]\)。对于\(N=\sumN[i]\)个人,每个人有一个属性\(P[i]\)和价值\(W[i]\),把第\(i\)个人放进第\(j\)个城市中,当且仅当\(P[i]>Q[j]\)时,可以获得\(W[i]\)的价值,否则不获得价值。求出满......