【XSY3139】预言家 数位DP NFA

题目描述

  有一个定义在 ({0,1,2,3,4,5,6,7,8,9}) 上的合规表达式,包含三种基本的操作:

   结合:(E_1E_2)

   分配:((E_1|E_2|ldots|E_n),ngeq 2)

   重复:((E_1)* ,ngeq 0)

  给你 (l,r),问你有多少个 ([l,r]) 之间不含前导零的整数能匹配这个合规表达式。

  (1leq lleq rleq {10}^{18})

题解

  直接建出这个合规表达式对应的 NFA,在上面跑数位 DP 即可。

  记录 (f_{i,0/1,j}) 表示还需要确定后 (i) 位,前面这几位是否比 (n) 小,在 NFA 上面可以达到的状态集合是 (j) 时的方案数。

  时间复杂度:(O(???))

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;
typedef long long ll;

char s[100];
int c[100];//另一个括号的位置
int n;
int st[100];
int top;
int b[100][100];//epsilon
int nxt[100];
int move(int x,int v)
{
	int res=0;
	for(int i=1;i<=n+1;i++)
		if(((x>>(i-1))&1)&&s[i]-'0'==v)
			res|=nxt[i+1];
	return res;
}
void init()
{
	memset(c,0,sizeof c);
	top=0;
	for(int i=1;i<=n;i++)
		if(s[i]=='(')
			st[++top]=i;
		else if(s[i]==')')
		{
			c[i]=st[top];
			c[st[top]]=i;
			top--;
		}
	memset(b,0,sizeof b);
	for(int i=1;i<=n+1;i++)
		b[i][i]=1;
	for(int i=1;i<=n;i++)
		if(s[i]=='(')
		{
			if(s[c[i]+1]=='*')
			{
				b[i][c[i]]=1;
				b[i][i+1]=1;
			}
			else
			{
				b[i][i+1]=1;
				int cnt=0;
				for(int j=i;j<=c[i];j++)
				{
					if(s[j]=='(')
						cnt++;
					else if(s[j]==')')
						cnt--;
					if(s[j]=='|'&&cnt==1)
					{
						b[i][j+1]=1;
						b[j][c[i]]=1;
					}
				}
			}
		}
		else if(s[i]==')')
		{
			b[i][i+1]=1;
			if(s[i+1]=='*')
				b[i][c[i]]=1;
		}
		else if(s[i]=='*')
			b[i][i+1]=1;

	for(int k=1;k<=n+1;k++)
		for(int i=1;i<=n+1;i++)
			for(int j=1;j<=n+1;j++)
				b[i][j]|=b[i][k]&&b[k][j];
	memset(nxt,0,sizeof nxt);
	for(int i=1;i<=n+1;i++)
		for(int j=1;j<=n+1;j++)
			if(b[i][j])
				nxt[i]|=1<<(j-1);
}
int len;
int a[100];
map<int,ll> f[100][2];
ll calc(ll m)
{
	if(!m)
		return 0;
	len=0;
	while(m)
	{
		a[++len]=m%10;
		m/=10;
	}
	for(int i=0;i<=len;i++)
	{
		f[i][0].clear();
		f[i][1].clear();
	}
	for(int i=1;i<=a[len];i++)
		f[len-1][i==a[len]][move(nxt[1],i)]++;
	for(int i=len-1;i>=1;i--)
		for(int j=1;j<=9;j++)
			f[i-1][0][move(nxt[1],j)]++;
	for(int i=len;i>=1;i--)
	{
		for(auto v:f[i][1])
			if(v.first)
			{
				 for(int j=0;j<=a[i];j++)
					 f[i-1][j==a[i]][move(v.first,j)]+=v.second;
			}
		for(auto v:f[i][0])
			if(v.first)
			{
				 for(int j=0;j<=9;j++)
					 f[i-1][0][move(v.first,j)]+=v.second;
			}
	}
	ll res=0;
	for(int i=0;i<=1;i++)
		for(auto v:f[0][i])
			if((v.first>>n)&1)
				res+=v.second;
	return res;
}
void solve()
{
	ll l,r;
	scanf("%lld%lld",&l,&r);
	scanf("%s",s+1);
	n=strlen(s+1);
	init();
	ll ans1=calc(r);
	ll ans2=calc(l-1);
	ll ans=ans1-ans2;
	printf("%lld
",ans);
}
int main()
{
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
#endif
	int t;
	scanf("%d",&t);
	while(t--)
		solve();
	return 0;
}
原文地址:https://www.cnblogs.com/ywwyww/p/9267567.html