[百度之星复赛T5]

题意大概就是给你一个字符串,求出最短的不是这个字符串的子串的长度和方案数,方案数对10^9+7取模。
input 1
abc

output 1
1 23

input 2
abcdefghijklmnopqrstuvwxyzzyxwvutsrqponmlkjihgfedcba

output 2
3 6201

N<=10^5

应该有两种方法。
①:dp
②:最短路。
说一下最短路怎么做吧。
我们从当前节点i依次向26个字母最早出现在i之后的位置连边。
如果i后面已经没有了某一个字母,那么就从i向失配节点连一条边。
我们再建立一个虚拟的源点分别向第一个出现的字母连边。
源点到失配节点的最短路就是第一个答案,第二个答案就是最短路的条数。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
#define D 1000000007
#define LL long long
const int N=100010;
LL sum[N];
char ch[N];
bool use[30],f[N];
struct S{int st,en;}aa[N*30];
int n,point[N],next[N*30],tot,now[30],dis[N],l[N*30];
vector <int> num[30];
inline void add(int x,int y){
    tot+=1;next[tot]=point[x];point[x]=tot;
    aa[tot].st=x;aa[tot].en=y;
}
void SPFA(int x){
    int i,u,h,t;
    memset(f,1,sizeof(f));
    memset(dis,127/3,sizeof(dis));
    h=t=1;l[h]=x;dis[x]=0;sum[x]=1;
    while(h<=t){
        u=l[h]; f[u]=true;
        for(i=point[u];i;i=next[i]){
            if(dis[aa[i].en]==dis[u]+1) sum[aa[i].en]=(sum[aa[i].en]+sum[u])%D;
            if(dis[aa[i].en]>dis[u]+1){
                dis[aa[i].en]=dis[u]+1;
                sum[aa[i].en]=sum[u];
                if(f[aa[i].en]){
                    f[aa[i].en]=false;
                    l[++t]=aa[i].en;
                }
            }
        }
        h+=1;
    }
}
int main(){
    freopen("code.in","r",stdin);
    freopen("code.out","w",stdout);
    int i,j,k;
    scanf("%s",&ch);
    n=strlen(ch);
    for(i=0;i<n;++i) num[ch[i]-'a'+1].push_back(i+1),use[ch[i]-'a'+1]=true;
    for(i=1;i<=n;++i){
        for(j=1;j<=26;++j){
            if(now[j]==n+1) continue;
            if((!(num[j].size()))||(!use[j])){
                now[j]=n+1;
                continue;
            }
            else{
                if((num[j][now[j]]>=i&&j!=ch[i-1]-'a'+1)||(num[j][now[j]]>i&&j==ch[i-1]-'a'+1)) continue;
                now[j]+=1;
                if(now[j]+1>num[j].size()) now[j]=n+1;
            }
        }
        for(j=1;j<=26;++j)
          add(i,now[j]==n+1?now[j]:num[j][now[j]]);
    }
    for(i=1;i<=26;++i){
        if(!use[i]) add(0,n+1);
        else add(0,num[i][0]);
    }
    SPFA(0);
    printf("%d %I64d\n",dis[n+1],sum[n+1]);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值