4.字符串

字符串

开题顺序: CHBA

A QOJ 8079.Range Periodicity Query

  • 设第 i 次操作后的字符串为 si 。注意到若 p 不是 si 的周期则 t 一定不是 sk(k>i) 的周期。

  • 对于每个 pi 作为周期的时间都是一段区间。由 luogu P3435 [POI2006] OKR-Periods of Words 结论,得到 s 有长度为 len 的 border$ 等价于 s 有长度为 |s|len 的周期。可以使用二分哈希辅助判断。

  • 询问转化为二维偏序问题,扫描线加线段树单点修改区间查询维护。

  • Gym 上会莫名 TLE ,加个超级快读就过了。

    点击查看代码
    namespace IO{
    	#ifdef LOCAL
    	FILE*Fin(fopen("test.in","r")),*Fout(fopen("test.out","w"));
    	#else
    	FILE*Fin(stdin),*Fout(stdout);
    	#endif
    	class qistream{static const size_t SIZE=1<<16,BLOCK=64;FILE*fp;char buf[SIZE];int p;public:qistream(FILE*_fp=stdin):fp(_fp),p(0){fread(buf+p,1,SIZE-p,fp);}void flush(){memmove(buf,buf+p,SIZE-p),fread(buf+SIZE-p,1,p,fp),p=0;}qistream&operator>>(char&x){x=getch();while(isspace(x))x=getch();return*this;}template<class T>qistream&operator>>(T&x){x=0;p+BLOCK>=SIZE?flush():void();bool flag=false;for(;!isdigit(buf[p]);++p)flag=buf[p]=='-';for(;isdigit(buf[p]);++p)x=x*10+buf[p]-'0';x=flag?-x:x;return*this;}char getch(){p+BLOCK>=SIZE?flush():void();return buf[p++];}qistream&operator>>(char*str){char ch=getch();while(ch<=' ')ch=getch();int i=0;for(;ch>' ';++i,ch=getch())str[i]=ch;str[i]='\0';return*this;}}qcin(Fin);
    	class qostream{static const size_t SIZE=1<<16,BLOCK=64;FILE*fp;char buf[SIZE];int p;public:qostream(FILE*_fp=stdout):fp(_fp),p(0){}~qostream(){fwrite(buf,1,p,fp);}void flush(){fwrite(buf,1,p,fp),p=0;}template<class T>qostream&operator<<(T x){int len=0;p+BLOCK>=SIZE?flush():void();x<0?(x=-x,buf[p++]='-'):0;do buf[p+len]=x%10+'0',x/=10,++len;while(x);for(int i=0,j=len-1;i<j;++i,--j)std::swap(buf[p+i],buf[p+j]);p+=len;return*this;}qostream&operator<<(char x){putch(x);return*this;}void putch(char ch){p+BLOCK>=SIZE?flush():void();buf[p++]=ch;}qostream&operator<<(char*str){for(int i=0;str[i];++i)putch(str[i]);return*this;}qostream&operator<<(const char*s){for(int i=0;s[i];++i)putch(s[i]);return*this;}}qcout(Fout);
    }
    #define cin IO::qcin
    #define cout IO::qcout
    const int mod=1000003579,base=13331,inf=0x7f7f7f7f;
    struct quality
    {
    	int l,r,id;
    };
    int hsh[500010],jc[500010],d[500010],st[500010],ed[500010],ans[500010];
    char s[500010];
    deque<char>t;
    vector<quality>ask[500010];
    vector<pair<int,int> >change[500010];
    void sx_hash(char s[],int len)
    {
    	for(int i=0;i<=len;i++)
    	{
    		hsh[i]=(i==0)?0:(1ll*hsh[i-1]*base%mod+s[i])%mod;
    	}
    } 
    int ask_hash(int l,int r)
    {
    	return (hsh[r]-1ll*hsh[l-1]*jc[r-l+1]%mod+mod)%mod;
    }
    int divide(int l,int r,int p)
    {
    	int mid=0,ans=l;
    	while(l<=r)
    	{
    		mid=(l+r)/2;
    		if(ask_hash(st[mid],st[mid]+(mid-p)-1)==ask_hash(ed[mid]-(mid-p)+1,ed[mid]))
    		{
    			ans=mid;
    			l=mid+1;
    		}
    		else
    		{
    			r=mid-1;
    		}
    	}
    	return ans;
    }
    struct SMT
    {
    	struct SegmentTree
    	{
    		int minn;
    	}tree[2000010];
    	#define lson(rt) (rt<<1)
    	#define rson(rt) (rt<<1|1)
    	void pushup(int rt)
    	{
    		tree[rt].minn=min(tree[lson(rt)].minn,tree[rson(rt)].minn);
    	}
    	void build(int rt,int l,int r)
    	{
    		tree[rt].minn=inf;
    		if(l==r)
    		{
    			return;
    		}
    		int mid=(l+r)/2;
    		build(lson(rt),l,mid);
    		build(rson(rt),mid+1,r);
    	}
    	void update(int rt,int l,int r,int pos,int val)
    	{
    		if(l==r)
    		{
    			tree[rt].minn=min(tree[rt].minn,val);
    			return;
    		}
    		int mid=(l+r)/2;
    		if(pos<=mid)
    		{
    			update(lson(rt),l,mid,pos,val);
    		}
    		else
    		{
    			update(rson(rt),mid+1,r,pos,val);
    		}
    		pushup(rt);
    	}
    	int query(int rt,int l,int r,int x,int y)
    	{
    		if(x<=l&&r<=y)
    		{
    			return tree[rt].minn;
    		}
    		int mid=(l+r)/2,ans=inf;
    		if(x<=mid)
    		{
    			ans=min(ans,query(lson(rt),l,mid,x,y));
    		}
    		if(y>mid)
    		{
    			ans=min(ans,query(rson(rt),mid+1,r,x,y));
    		}
    		return ans;
    	}
    }T;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,m,q,l,r,k,i,j;
    	cin>>n>>(s+1);
    	jc[0]=1;
    	for(i=1;i<=n;i++)
    	{
    		jc[i]=1ll*jc[i-1]*base%mod;
    		if('a'<=s[i]&&s[i]<='z')
    		{
    			d[1]++;
    			d[i]--;
    			t.push_front(s[i]);
    		}
    		else
    		{
    			s[i]-='A'-'a';
    			t.push_back(s[i]);
    		}
    	}
    	for(i=1;i<=n;i++)
    	{
    		s[i]=t.front();
    		t.pop_front();
    	}
    	sx_hash(s,n);
    	for(i=1;i<=n;i++)
    	{
    		d[i]+=d[i-1];
    		st[i]=1+d[i];
    		ed[i]=i+d[i];
    	}
    	cin>>m;
    	for(i=1;i<=m;i++)
    	{
    		cin>>k;
    		change[divide(k,n,k)].push_back(make_pair(k,i));
    	}
    	cin>>q;
    	for(i=1;i<=q;i++)
    	{
    		cin>>k>>l>>r;
    		ask[k].push_back((quality){l,r,i});
    	}
    	T.build(1,1,m);
    	for(i=n;i>=1;i--)
    	{
    		for(j=0;j<change[i].size();j++)
    		{
    			T.update(1,1,m,change[i][j].second,change[i][j].first);
    		}
    		for(j=0;j<ask[i].size();j++)
    		{
    			k=T.query(1,1,m,ask[i][j].l,ask[i][j].r);
    			ans[ask[i][j].id]=((k<=i)?k:-1);
    		}
    	}
    	for(i=1;i<=q;i++)
    	{
    		cout<<ans[i]<<endl;
    	}
    	return 0;
    }
    

B QOJ 8701.Border

  • 考虑枚举 len ,二分哈希求出 LCP(s1len,snlen+1,n) 得到第一处不同的位置,这个位置必须被修改且修改后 s1len=snlen+1,n 。若找不到不同的位置且 len<nlen+1 说明 [len+1,nlen] 的答案至少为 len ,打个标记即可。

  • 特判 si=ti 时答案为原串 border 长度。

    点击查看代码
    const ll mod=1000003579,base=13331;
    ll a[2000010],jc[2000010],ans[2000010],lazy[2000010];
    char s[2000010],t[2000010];
    void sx_hash(char s[],ll len)
    {
    	for(ll i=0;i<=len;i++)
    	{
    		a[i]=(i==0)?0:(a[i-1]*base%mod+s[i])%mod;
    	}
    }
    ll ask_hash(ll l,ll r)
    {
    	return (l<=r)?(a[r]-a[l-1]*jc[r-l+1]%mod+mod)%mod:0;
    }
    ll change_hash(ll l,ll r,ll pos)
    {
    
    	return (l<=pos&&pos<=r)?(ask_hash(l,r)+(t[pos]-s[pos])*jc[r-pos]%mod+mod)%mod:ask_hash(l,r);
    }
    ll lcp(ll x,ll y,ll len)
    {
    	ll l=1,r=len,mid,ans=0;
    	while(l<=r)
    	{
    		mid=(l+r)/2;
    		if(ask_hash(x,x+mid-1)==ask_hash(y,y+mid-1))
    		{
    			ans=mid;
    			l=mid+1;
    		}
    		else
    		{
    			r=mid-1;
    		}
    	}
    	return ans;
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	ll n,len,border=0,i;
    	cin>>(s+1)>>(t+1);
    	n=strlen(s+1);
    	jc[0]=1;
    	sx_hash(s,n);
    	for(i=1;i<=n;i++)
    	{
    		jc[i]=jc[i-1]*base%mod;
    	}
    	for(i=1;i<=n-1;i++)
    	{
    		border=(ask_hash(1,i)==ask_hash(n-i+1,n))?i:border;
    		len=lcp(1,n-i+1,i);
    		if(len!=i)
    		{
    			if(s[len+1]!=t[len+1]&&change_hash(1,i,len+1)==change_hash(n-i+1,n,len+1))
    			{
    				ans[len+1]=i;
    			}
    			if(s[n-i+1+len]!=t[n-i+1+len]&&change_hash(1,i,n-i+1+len)==change_hash(n-i+1,n,n-i+1+len))
    			{
    				ans[n-i+1+len]=i;
    			}
    		}
    		else
    		{
    			if(i<n-i+1)
    			{
    				lazy[i+1]=lazy[n-i]=i;
    			}
    		}
    	}
    	for(i=1;i<=n&&i<=n-i+1;i++)
    	{
    		lazy[i]=max(lazy[i],lazy[i-1]);
    	}
    	for(i=n;i>=1&&i>=n-i+1;i--)
    	{
    		lazy[i]=max(lazy[i],lazy[i+1]);
    	}
    	for(i=1;i<=n;i++)
    	{
    		cout<<((s[i]==t[i])?border:max(ans[i],lazy[i]))<<endl;
    	}
    	return 0;
    }
    

C luogu P2375 [NOI2014] 动物园

D QOJ 6842.Popcount Words

E [AGC064C] Erase and Divide Game

F LibreOJ 6070. 「2017 山东一轮集训 Day4」基因

G CF906E Reverses

H luogu P1117 [NOI2016] 优秀的拆分

  • AABB 串可以看做两个 AA 串拼接起来,考虑求出以 i 结尾和开头的 AA 串数量 f,g

  • 枚举 A 长度 len 后再枚举左右端点使用后缀数组加速 LCP 计算的时间复杂度为 O(n2) ,需要进一步优化。

  • 仍枚举长度 len ,并将 len 的倍数的位置设置为关键点,则 AA 串必然经过两个关键点。设当前枚举的相邻两个关键点分别为 x,y ,考虑统计经过这两个关键点的数量。

  • 恰好取到端点处的答案是容易处理的。端点在 x 左边和 y 右边时需要保证 LCP(sxn,syn)+LCS(s1x1,s1y1)len 。设 lcp=LCP(sxn,syn),lcs=LCS(s1x1,s1y1) ,此时 [y+lcp(lcp+lcslen)1,y+lcp1] 均可以作为 AA 串的结尾, [(x1)lcs+1,(x1)lcs+(lcp+lcslen)+1] 均可以作为 AA 串的开头。

    • 建议画图理解。
  • 差分完前缀和统计答案即可。

    点击查看代码
    ll f[30010],g[30010],n;
    char s[30010];
    struct SA
    {	
    	struct ST
    	{
    		ll fminn[30010][20];
    		void init(ll n,ll a[])
    		{
    			memset(fminn,0x3f,sizeof(fminn));
    			for(ll i=1;i<=n;i++)
    			{
    				fminn[i][0]=a[i];
    			}
    			for(ll j=1;j<=log2(n);j++)
    			{
    				for(ll i=1;i+(1<<j)-1<=n;i++)
    				{
    					fminn[i][j]=min(fminn[i][j-1],fminn[i+(1<<(j-1))][j-1]);
    				}
    			}
    		}
    		ll query(ll l,ll r)
    		{
    			ll t=log2(r-l+1);
    			return min(fminn[l][t],fminn[r-(1<<t)+1][t]);
    		}
    	}T;
    	ll sa[30010],rk[60010],oldrk[60010],id[30010],cnt[30010],key[30010],height[30010];
    	ll val(char x)
    	{
    		return (ll)x;
    	}
    	void counting_sort(ll n,ll m)
    	{
    		memset(cnt,0,sizeof(cnt));
    		for(ll i=1;i<=n;i++)
    		{
    			cnt[key[i]]++;
    		}
    		for(ll i=1;i<=m;i++)
    		{
    			cnt[i]+=cnt[i-1];
    		}
    		for(ll i=n;i>=1;i--)
    		{
    			sa[cnt[key[i]]]=id[i];
    			cnt[key[i]]--;
    		}
    	}
    	void init(char s[],ll len)
    	{
    		ll m=127,tot=0,num=0;
    		for(ll i=1;i<=len;i++)
    		{
    			rk[i]=val(s[i]);
    			id[i]=i;
    			key[i]=rk[id[i]];
    		}
    		counting_sort(len,m);
    		for(ll w=1;tot!=len;w<<=1,m=tot)
    		{
    			num=0;
    			for(ll i=len;i>=len-w+1;i--)
    			{
    				num++;
    				id[num]=i;
    			}
    			for(ll i=1;i<=len;i++)
    			{
    				if(sa[i]>w)
    				{
    					num++;
    					id[num]=sa[i]-w;
    				}
    			}
    			for(ll i=1;i<=len;i++)
    			{
    				key[i]=rk[id[i]];
    			}
    			counting_sort(len,m);
    			for(ll i=1;i<=len;i++)
    			{
    				oldrk[i]=rk[i];
    			}
    			tot=0;
    			for(ll i=1;i<=len;i++)
    			{
    				tot+=(oldrk[sa[i]]!=oldrk[sa[i-1]]||oldrk[sa[i]+w]!=oldrk[sa[i-1]+w]);
    				rk[sa[i]]=tot;
    			}
    		}
    		for(ll i=1,j=0;i<=len;i++)
    		{
    			j-=(j>=1);
    			while(s[i+j]==s[sa[rk[i]-1]+j])
    			{
    				j++;
    			}
    			height[rk[i]]=j;
    		}
    		T.init(len,height);
    	}
    	ll lcp(ll x,ll y)
    	{
    		if(rk[x]>rk[y])
    		{
    			swap(x,y);
    		}
    		return (x==y)?n-x+1:T.query(rk[x]+1,rk[y]);
    	}
    }F,B;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	ll t,ans,len,lcp,lcs,i,j,k;
    	cin>>t;
    	for(k=1;k<=t;k++)
    	{
    		ans=0;
    		memset(f,0,sizeof(f));
    		memset(g,0,sizeof(g));
    		cin>>(s+1);
    		n=strlen(s+1);
    		F.init(s,n);
    		reverse(s+1,s+1+n);
    		B.init(s,n);
    		for(len=1;len<=n/2;len++)
    		{
    			for(i=len,j=i+len;j<=n;i+=len,j+=len)
    			{
    				lcp=min(len,F.lcp(i,j));
    				lcs=min(len-1,B.lcp(n-(i-1)+1,n-(j-1)+1));
    				if(lcp+lcs>=len)
    				{
    					f[j+lcp-(lcs+lcp-len)-1]++;
    					f[j+lcp]--;
    					g[(i-1)-lcs+1]++;
    					g[(i-1)-lcs+(lcs+lcp-len)+2]--;
    				}
    			}
    		}
    		for(i=1;i<=n;i++)
    		{
    			f[i]+=f[i-1];
    			g[i]+=g[i-1];
    		}
    		for(i=1;i<=n-1;i++)
    		{
    			ans+=f[i]*g[i+1];
    		}
    		cout<<ans<<endl;
    	}
    	return 0;
    }
    

I luogu P9482 [NOI2023] 字符串

J luogu P9623 [ICPC2020 Nanjing R] Baby's First Suffix Array Problem

K [ARC175F] Append Same Characters

L luogu P4218 [CTSC2010] 珠宝商

M CF1276F Asterisk Substrings

N CF1817F Entangled Substrings

O UOJ 577.【ULR #1】打击复读

P UOJ 697.【候选队互测2022】广为人知题

Q QOJ 9639.字符串

R CF1393E2 Twilight and Ancient Scroll (harder version)

S luogu P4482 [BJWC2018] Border 的四种求法

T luogu P8351 [SDOI/SXOI2022] 子串统计

posted @   hzoi_Shadow  阅读(77)  评论(4编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
扩大
缩小
点击右上角即可分享
微信分享提示