zoukankan      html  css  js  c++  java
  • SDOI2014 数数

    数数

    我们称一个正整数N是幸运数,当且仅当它的十进制表示中不包含数字串集合S中任意一个元素作为其子串。例如当S=(22,333,0233)时,233是幸运数,2333、20233、3223不是幸运数。

    给定N和S,计算不大于N的幸运数个数。

    租酥雨的题解

    AC自动机做数位DP。首先位数小于(n)的位数的数只要满足没有不合法串即可,记(f[i][j])表示填了(i)个数,当前在AC自动机上编号为(j)的节点上的方案数,取答案(sum^{n−1}_{i=1}sum^{tot}_{j=0}f[i][j])。注意转移的时候是只能转移到自己Trie图上的儿子而不是儿子通过fail指针串起来的所有点。

    位数等于(n)的,在前面那个状态上多加一维,表示是否已经严格小于那个数,然后按照数位DP的一般思路卡一卡就好了。

    第二次DP的状态是(f[0/1][i][j]),0/1是数位dp的那个是否之前所有位都和N相同。

    时间复杂度(O(10 * l * L)),是18000000。

    co int mod=1e9+7,N=1501;
    
    int add(int x,int y)
    {
    	x+=y;
    	return x>mod?x-mod:x;
    }
    
    int n;
    char s[N],buf[N];
    namespace AC
    {
    	int tot;
    	int ch[N][10],val[N],fail[N];
    	
    	void ins(char s[],int n)
    	{
    		int u=0;
    		for(int i=0;i<n;++i)
    		{
    			int k=s[i]-'0';
    			if(!ch[u][k])
    				ch[u][k]=++tot;
    			u=ch[u][k];
    		}
    		val[u]=1;
    	}
    	
    	void getfail()
    	{
    		std::queue<int>Q;
    		for(int i=0;i<10;++i)
    			if(ch[0][i])
    				Q.push(ch[0][i]);
    		while(Q.size())
    		{
    			int u=Q.front();Q.pop();
    			val[u]|=val[fail[u]];
    			for(int i=0;i<10;++i)
    			{
    				if(ch[u][i])
    				{
    					fail[ch[u][i]]=ch[fail[u]][i];
    					Q.push(ch[u][i]);
    				}
    				else
    					ch[u][i]=ch[fail[u]][i];
    			}
    		}
    	}
    	
    	int dp[2][N][N];
    	
    	void solve()
    	{
    		int ans=0;
    		dp[0][0][0]=1;
    		for(int i=0;i<n;++i)
    			for(int j=0;j<=tot;++j)if(!val[j])
    				for(int k=0;k<10;++k)if(i+k&&!val[ch[j][k]])
    					dp[0][i+1][ch[j][k]]=add(dp[0][i+1][ch[j][k]],dp[0][i][j]);
    		for(int i=1;i<n;++i)
    			for(int j=0;j<=tot;++j)
    				ans=add(ans,dp[0][i][j]);
    		memset(dp,0,sizeof dp);
    		dp[1][0][0]=1;
    		for(int i=0;i<n;++i)
    			for(int j=0;j<=tot;++j)if(!val[j])
    				for(int k=0;k<10;++k)if(i+k&&!val[ch[j][k]])
    				{
    					dp[0][i+1][ch[j][k]]=add(dp[0][i+1][ch[j][k]],dp[0][i][j]);
    					if(k==s[i+1]-'0')
    						dp[1][i+1][ch[j][k]]=add(dp[1][i+1][ch[j][k]],dp[1][i][j]);
    					else if(k<s[i+1]-'0')
    						dp[0][i+1][ch[j][k]]=add(dp[0][i+1][ch[j][k]],dp[1][i][j]);
    				}
    		for(int j=0;j<=tot;++j)
    			ans=add(ans,add(dp[0][n][j],dp[1][n][j]));
    		printf("%d
    ",ans);
    	}
    }
    
    int main()
    {
    //	freopen(".in","r",stdin);
    //	freopen(".out","w",stdout);
    	scanf("%s",s+1);
    	n=strlen(s+1);
    	int m;
    	read(m);
    	for(int i=1;i<=m;++i)
    	{
    		scanf("%s",buf);
    		AC::ins(buf,strlen(buf));
    	}
    	AC::getfail();
    	AC::solve();
    	return 0;
    }
    
  • 相关阅读:
    Leetcode 274.H指数
    Leetcode 273.整数转换英文表示
    Leetcode 264.丑数II
    Leetcode 260.只出现一次的数字III
    Leetcode 242.有效的字母异位词
    Leetcode 241.为运算表达式设计优先级
    Leetcode 240.搜索二维矩阵II
    Leetcode 239.滑动窗口最大值
    Leetcode 237.删除链表中的节点
    Leetcode 236.二叉树的最近公共祖先
  • 原文地址:https://www.cnblogs.com/autoint/p/10323326.html
Copyright © 2011-2022 走看看