1. 程式人生 > >[百度之星複賽T5]

[百度之星複賽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]); }