1. 程式人生 > 實用技巧 >Leetcode <622.設計迴圈佇列>

Leetcode <622.設計迴圈佇列>

一、題目

點此看題

二、解法

今天不知道為什麼手感這麼好,寫一發完全沒調就過掉了。

我感覺這種多組詢問的字串題是很難的,經常沒有什麼思路。我先考慮了一下能不能像 區間本質不同的子串個數 這樣直接離線,但我想了很久發現不行的。

正確的做法是很天馬行空的,我們 考慮預處理出 \(s\) 的所有子串在 \(t\) 中有沒有出現 ,列舉是不可能列舉的,要把他們壓在一起快速的處理。設 \(R[i]\)\([i,R[i]]\)\(t\) 中出現過,那麼對於比 \(R[i]\) 小的右端點是一定出現過的,比 \(R[i]\) 大的右端點是沒有出現過的。

\(R[i]\) 用字尾自動機可以快速處理,因為 \([i,R[i]]\)

是在 \(t\) 中出現過的,\([i+1,R[i]]\) 也是在 \(t\) 中出現過的,那麼 \(R[i+1]\geq R[i]\),所以說我們可以暴力跳轉移,當發現長度不適應字尾自動機上的這個點時,就可以跳字尾樹上的父親。不難發現時間複雜度是 \(O(n)\) 的。

知道了 \(R[i]\) 之後,詢問 \((l,r)\) 的答案很容易寫出來:

\[\max_{i=l}^r (\min(R[i],r)-i+1) \]

這個式子乍看上去沒有辦法優化,但別忘了我們還有一個法寶:離線 。如果你覺得裡面的 \(\min\) 特別噁心那麼我們可以分類討論來去掉這個 \(\min\)

  • \(R[i]\leq r\)
    ,那麼裡面的柿子就變成了:\(R[i]-i+1\)
  • \(R[i]>r\),那麼裡面的柿子就變成了:\(r-i+1\)

拆掉 \(\min\) 之後問題變成了二維偏序之類的東西,解決他的固定套路就是 排序降維 。那麼我們把 \(R[i],r\) 都從小到大排序,然後維護兩顆線段樹,一顆維護 \(-i\) 的最大值,一顆維護 \(R[i]-i+1\) 就可以了。

時間複雜度 \(O(n\log n)\)

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int M = 200005;
const int inf = -1e9;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,k,cnt,last,R[M],ans[M];char s[M],t[M];
int mx[2][4*M];
struct node
{
	int fa,len,ch[2];
}a[2*M];
struct reg//regret
{
	int l,r,id;
	bool operator < (const reg &R) const
	{
		return r<R.r;
	}
}b[M],q[M];
void add(int c)
{
	int p=last,np=last=++cnt;
	a[np].len=a[p].len;
	for(;p && !a[p].ch[c];p=a[p].fa) a[p].ch[c]=np;
	if(!p) a[np].fa=1;
	else
	{
		int q=a[p].ch[c];
		if(a[p].len+1==a[q].len) a[np].fa=q;
		else
		{
			int nq=++cnt;a[nq]=a[q];
			a[nq].len=a[p].len+1;
			a[np].fa=a[q].fa=nq;
			for(;p && a[p].ch[c]==q;p=a[p].fa) a[p].ch[c]=nq;
		}
	}
}
void ins(int i,int l,int r,int id,int v,int f)
{
	if(l==r)
	{
		mx[f][i]=v;
		return ;
	}
	int mid=(l+r)>>1;
	if(mid>=id) ins(i<<1,l,mid,id,v,f);
	else ins(i<<1|1,mid+1,r,id,v,f);
	mx[f][i]=max(mx[f][i<<1],mx[f][i<<1|1]);
}
int ask(int i,int l,int r,int L,int R,int f)
{
	if(L>r || l>R) return inf;
	if(L<=l && r<=R) return mx[f][i];
	int mid=(l+r)>>1;
	return max(ask(i<<1,l,mid,L,R,f),ask(i<<1|1,mid+1,r,L,R,f));
}
signed main()
{
	scanf("%s %s",s+1,t+1);
	n=strlen(s+1);m=strlen(t+1);
	cnt=last=1;//attention
	for(int i=1;i<=m;i++)
		add(t[i]-'a');
	for(int i=1,p=1;i<=n;i++)
	{
		int r=min(i-1,R[i-1]);
		if(r==i-1) p=1;
		while(p!=1 && a[a[p].fa].len>r-i+1) p=a[p].fa;
		while(r<n && a[p].ch[s[r+1]-'a'])
		{
			r++;
			p=a[p].ch[s[r]-'a'];
		}
		R[i]=r;
		b[i]=reg{i,r,0};
	}
	sort(b+1,b+1+n);
	k=read();
	for(int i=1;i<=k;i++)
	{
		int l=read(),r=read();
		q[i]=reg{l,r,i};
	}
	sort(q+1,q+1+k);
	memset(mx,-0x3f,sizeof mx);
	for(int i=1;i<=n;i++)
		ins(1,1,n,i,-i,0);
	for(int i=1,j=1;i<=k;i++)
	{
		int l=q[i].l,r=q[i].r,id=q[i].id;
		while(j<=n && b[j].r<=r)
		{
			ins(1,1,n,b[j].l,inf,0);
			ins(1,1,n,b[j].l,b[j].r-b[j].l+1,1);
			j++;
		}
		ans[id]=max(ask(1,1,n,l,r,1),r+ask(1,1,n,l,r,0)+1);
		ans[id]=max(0,ans[id]);
	}
	for(int i=1;i<=k;i++)
		printf("%d\n",ans[i]);
}