[百度之星複賽T5]
阿新 • • 發佈:2019-01-29
題意大概就是給你一個字串,求出最短的不是這個字串的子串的長度和方案數,方案數對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]);
}