Living-Dream 系列笔记 第59期

T1

这是一道 manacher 模板,但是我们使用 二分 + hash \(O(n \log n)\) 的做法。

显然地,若长为 \(len\) 的回文串存在,则长为 \(len-2,len-4,...\) 的回文串也一定存在(在两端各删去若干相同字符即可)。

至此,我们发现回文串分两类:奇回文串、偶回文串,在每一类当中分别具有单调性。

于是我们对于同一对称轴,进行两遍二分,check 用 hash 判回文即可。

奇、偶回文串对于答案的贡献即为 \(l \times 2\)\(l \times 2 + 1\)

于是我们得到了 24 pts。

code
#include<bits/stdc++.h>
#define ull unsigned long long
#define int long long
using namespace std;

const int N=1e7+5;
const int BASE=13331;
int n,ans=-1e9;
string s,t;
ull hs[N],ht[N],p[N];

void init_hash(){
	p[0]=1;
	for(int i=1;i<=n;i++) p[i]=p[i-1]*BASE;
	for(int i=1;i<=n;i++) hs[i]=hs[i-1]*BASE+s[i];
	for(int i=n;i>=1;i--) ht[i]=ht[i+1]*BASE+s[i];
}
int get_hs(int l,int r){
	return hs[r]-hs[l-1]*p[r-l+1];
}
int get_ht(int l,int r){
	return ht[l]-ht[r+1]*p[r-l+1];
}

signed main(){
	cin>>s,n=s.size(),s='#'+s;
	init_hash();
	for(int i=1;i<=n;i++){
		int l=0,r=min(i-1,n-i+1)+1; 
		while(l+1<r){
			int mid=(l+r)>>1;
			if(get_hs(i-mid,i-1)==get_ht(i,i+mid-1)) l=mid;
			else r=mid;
		}
		ans=max(ans,l*2);
		l=0,r=min(i-1,n-i)+1;
		while(l+1<r){
			int mid=(l+r)>>1;
			if(get_hs(i-mid,i-1)==get_ht(i+1,i+mid)) l=mid;
			else r=mid;
		}
		ans=max(ans,l*2+1);
	}
	cout<<ans;
	return 0;
} 

T2/T3

很妙的一道题阿。

我们钦定含通配符的字符串为 \(S\),文件名列表里的当前字符串为 \(T\)

我们注意到询问仅关心是否匹配上了,考虑 dp。

我们又发现通配符个数 \(\le 10\),考虑按通配符划分状态。

\(dp_{i,j}\) 表示 \(S\) 以第 \(i\) 个通配符结尾,\(T\) 以第 \(j\) 个字符结尾时是否能匹配上。

初始状态:\(dp_{0,0}=1\),答案:判断 \(dp_{tot,\mid T \mid}\)\(tot\) 表示 \(S\) 中通配符个数,\(|T|\) 表示 \(T\) 的长度)。

转移:

  • 若当前通配符为 *

    \(dp_{i,j-1}=1\),则 \(dp_{i,j}=1\)(不停往下扩展,因为 * 可以包含任意个字符)

  • 否则

    枚举与之匹配的 \(T\) 中的与其长度相同的子串,用 hash 检测是否相同,

    • 若下一个通配符为 ?

      \(dp_{i+1,j+len+1} = 1\)(延长一个也可匹配成功)

    • 否则

      \(dp_{i+1,j+len}=1\)

一些细节:

image

image

code
#include<bits/stdc++.h>
#define ull unsigned long long
using namespace std;

const int N=1e5+5,M=15;
const int BASE=13331;
int n;
ull hs[N],ht[N],p[N];
bool dp[M][N];
string s;
vector<int> pos;

void init_hash(){
	p[0]=1;
	for(int i=1;i<=N;i++) p[i]=p[i-1]*BASE;
	for(int i=1;i<s.size();i++) hs[i]=hs[i-1]*BASE+s[i];
}
ull get_hs(int l,int r){
	return hs[r]-hs[l-1]*p[r-l+1];
}
ull get_ht(int l,int r){
	return ht[r]-ht[l-1]*p[r-l+1];
}
string sol(string t){
	memset(dp,0,sizeof(dp)),dp[0][0]=1;
	t='#'+t+'#';
	for(int i=1;i<t.size();i++) ht[i]=ht[i-1]*BASE+t[i];
	for(int i=0;i<pos.size();i++){
		if(s[pos[i]]=='*')
			for(int j=1;j<t.size();j++)
				if(dp[i][j-1]) dp[i][j]=1;
		for(int j=0;j<t.size();j++){
			if(!dp[i][j]) continue;
			int st=pos[i]+1,ed=pos[i+1]-1,len=ed-st+1;
			if(j+len<t.size()&&get_hs(st,ed)==get_ht(j+1,j+len)){
				if(s[pos[i+1]]=='?') dp[i+1][j+len+1]=1;
				else dp[i+1][j+len]=1;
			}
		}
	}
	if(dp[pos.size()-1][t.size()-1]) return "YES";
	else return "NO"; 
}

int main(){
	cin>>s>>n;
	s='#'+s+'?',pos.push_back(0);
	for(int i=1;i<s.size();i++)
		if(s[i]=='*'||s[i]=='?') pos.push_back(i);
	init_hash();
	while(n--){
		string t; cin>>t;
		cout<<sol(t)<<'\n';
	}
	return 0;
}

T3 是双倍经验。

作业 T1

求方案数,考虑 dp。

我们钦定碱基串为 \(S\),当前选的碱基序列为 \(T\)

\(dp_{i,j}\) 表示前 \(i\) 个集合选出的字符串拼接起来后与 \(s_{1 \sim j}\) 的匹配方案数。

初始状态:\(dp_{0,i}=1\),答案:\(\sum dp_{k,i}\)

(关于初始状态为什么不是 \(dp_{0,0}=1\),这是因为题目仅要求 \(T\)\(S\) 的子串而非前缀)

转移:

\[dp_{i,j}=(dp_{i,j}+dp_{i-1,j-len}) \bmod 10^9 +7 \]

\(len\)\(T\) 的长度)

条件:

\[hs(j-len+1,j)=ht \]

\(hs(l,r)\) 表示 \(S_{l \sim r}\) 的 hash value,\(ht\) 表示 \(T\) 的 hash value)

code
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
using namespace std;

const int K=1e2+5,A=11,S=1e4+5;
const int MOD=1e9+7,BASE=13331;
int n,k,ans;
int dp[K][S],a[K];
ull hs[S],ht[K][A],p[S];
string s,t[K][A];

void init_hash(){
	p[0]=1;
	for(int i=1;i<=n;i++) p[i]=p[i-1]*BASE;
	for(int i=1;i<=n;i++) hs[i]=hs[i-1]*BASE+s[i];
	for(int i=1;i<=k;i++)
		for(int j=1;j<=a[i];j++)
			for(int p=1;p<t[i][j].size();p++)
				ht[i][j]=ht[i][j]*BASE+t[i][j][p];
}
ull get_hs(int l,int r){
	return hs[r]-hs[l-1]*p[r-l+1];
}

signed main(){
	cin>>k>>s,n=s.size(),s='#'+s;
	for(int i=1;i<=k;i++){
		cin>>a[i];
		for(int j=1;j<=a[i];j++)
			cin>>t[i][j],t[i][j]='#'+t[i][j];
	}
	init_hash();
	for(int i=0;i<=n;i++) dp[0][i]=1;
	for(int i=1;i<=k;i++){
		for(int j=1;j<=n;j++){
			for(int u=1;u<=a[i];u++){
				int len=t[i][u].size()-1;
				if(j>=len&&get_hs(j-len+1,j)==ht[i][u])
					dp[i][j]=(dp[i][j]+dp[i-1][j-len])%MOD;
			}
		}
	}
	for(int i=1;i<=n;i++) ans=(ans+dp[k][i])%MOD;
	cout<<ans;
	return 0;
}
posted @ 2024-06-08 21:54  _KidA  阅读(10)  评论(0)    收藏  举报