Codeforces 585F

这篇博客讨论了一道名为Codeforces 585F的迷惑性问题,指出其3200分的难度设置可能不合理。博主提出了解决方案,通过构建AC自动机来计算不含特定长度子串的字符串数量,并使用数位DP进行求解。预处理过程中,博主计算了在AC自动机上从特定节点出发,经过一定步数仍不包含特定子串的路径数,整体算法的时间复杂度为O(∣s∣d²⋅10)。
摘要由CSDN通过智能技术生成

真实迷惑题,这个3200分不知道是怎么标上去的。
考虑计算出不是half-occurrence的串数目。把所有 s s s长度为 ⌊ d 2 ⌋ \lfloor \frac{d}{2}\rfloor 2d的子串插入一个AC自动机,然后对 x x x y y y做个数位DP即可。DP的时候需要预处理出 F [ i ] [ j ] F[i][j] F[i][j]表示当前在AC自动机上的节点 i i i,走 j j j步后仍然没有包含任何一个 s s s长度为 ⌊ d 2 ⌋ \lfloor \frac{d}{2}\rfloor 2d的子串的方案数,这个直接算就好了。
时间复杂度 O ( ∣ s ∣ d 2 ⋅ 10 ) \mathcal O(|s|d^2\cdot 10) O(sd210)

#include <bits/stdc++.h>
#define MOD 1000000007

using namespace std;

inline void add(int &x,int y) {
  ((x+=y)>=MOD)?x-=MOD:0;
}

namespace AC {

int ch[25005][10],tot=1;
bool ban[25005];

void insert(char *s,int len) {
  int x=1;
  for(int i=0;i<len;i++) {
  	int v=s[i]-'0';
  	if (!ch[x][v]) ch[x][v]=++tot;
  	x=ch[x][v];
  }
  ban[x]=1;
}

int fail[25005];
queue <int> q;

void bfs() {
  for(int i=0;i<10;i++)
    if (ch[1][i]) {
    	fail[ch[1][i]]=1;
    	q.push(ch[1][i]);
	}
	else ch[1][i]=1;
  while (!q.empty()) {
  	int x=q.front();q.pop();
  	for(int i=0;i<10;i++)
  	  if (ch[x][i]) {
  	  	  fail[ch[x][i]]=ch[fail[x]][i];
  	  	  q.push(ch[x][i]);
		}
	  else ch[x][i]=ch[fail[x]][i];
  }
}

int f[55][25005];

void dp(int n) {
  for(int i=1;i<=tot;i++)
    if (!ban[i]) f[0][i]=1;
  for(int i=1;i<=n;i++)
    for(int j=1;j<=tot;j++)
      if (!ban[j]) {
      	for(int k=0;k<10;k++)
      	  if (f[i-1][ch[j][k]]) add(f[i][j],f[i-1][ch[j][k]]);
	  }
}
 
}

int solve(int n,char *s) {
  int x=1,ans=0;
  for(int i=n;i>0;i--) {
  	int v=s[i]-'0';
  	for(int j=0;j<v;j++)
  	  if (!AC::ban[AC::ch[x][j]]) add(ans,AC::f[i-1][AC::ch[x][j]]);
  	x=AC::ch[x][v];
  	if (AC::ban[x]) break;
  }
  if (!AC::ban[x]) add(ans,1);
  return ans;
}

char str[1005],s1[55],s2[55];

int main() {
  scanf("%s%s%s",str+1,s1+1,s2+1);
  int n=strlen(str+1),m=strlen(s1+1);
  reverse(s1+1,s1+m+1);
  reverse(s2+1,s2+m+1);
  for(int i=1;i<=n-(m>>1)+1;i++) AC::insert(str+i,m>>1);
  AC::bfs();
  AC::dp(m);
  int d=1;
  while (s1[d]=='0') d++;
  s1[d]--;
  for(int i=1;i<d;i++) s1[i]='9';
  int ans=0,s=0;
  for(int i=m;i>0;i--) s=(s*10LL+s1[i]-'0')%MOD;
  ans=(ans-s+MOD)%MOD;
  ans=(ans+solve(m,s1))%MOD;
  s=0;
  for(int i=m;i>0;i--) s=(s*10LL+s2[i]-'0')%MOD;
  ans=(ans+s)%MOD;
  ans=(ans-solve(m,s2)+MOD)%MOD;
  printf("%d\n",ans);
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值