1. 程式人生 > >bzoj1729:[Usaco2005 dec]Cow Patterns 牛的模式匹配(kmp+思維)

bzoj1729:[Usaco2005 dec]Cow Patterns 牛的模式匹配(kmp+思維)

Problem

有一個 n 個數的數列 A,其數字範圍為 1~k(k<=25) 。 還有 m 個數 B。 問從數列 n 數列取出連續 m 個數,排名與 B 中排名一致的情況有多少種

Solution

我們考慮這個 B,將其變成所以可能的序列,然後和原串進行 kmp 可能的序列可以用遞迴求解… 然而這樣肯定複雜度大啊!

所以我們改進…(改進完和原來就沒啥關係了…) 但本質還是改變模式串。 先來一波定義,定義 B 中有 k 個數

在求這個模式串的過程中,要是每位都去重新考慮就次數太多了… 我們將模式串進行一波離散化 那我們列舉模式串中的數字對應數列

A 中為幾,然後判斷從哪些位開始的在模式串這個數字中成立。結果就是所以模式串每一個數字都模擬出來,且都包含的結果

說不清..上程式碼吧…

Code

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 100010
#define inf 0x3f3f3f3f
int n,m,k,ans=0,nxt[N],f[N],s[N],t[N],a[N],b[N],c[N],maxx[N],maxy[N];
inline void KMP(int
step,int x){ for(int i=1;i<=n;i++) if(a[i]==x) s[i]=x;else s[i]=0; for(int i=1;i<=m;i++) if(b[i]==step) t[i]=x;else t[i]=0; nxt[1]=0; for(int i=2,j=0;i<=m;i++){ while(j && t[i]!=t[j+1]) j=nxt[j]; if(t[i]==t[j+1]) j++; nxt[i]=j; } for(int
i=1,j=0;i<=n;i++){ while(j && (j==m || s[i]!=t[j+1])) j=nxt[j]; if(s[i]==t[j+1]) j++; if(j==m) maxy[i-m+1]=x; } } int main(){ while(scanf("%d%d%d",&n,&m,&k)>0){ for(int i=1;i<=n;i++) scanf("%d",&a[i]); memset(c,0,sizeof(c)); for(int i=1;i<=m;i++) scanf("%d",&b[i]),c[b[i]]=1; memset(maxx,0,sizeof(maxx)); for(int i=1;i<=k;i++){ if(!c[i]) continue; for(int j=1;j<=n;j++) maxy[j]=inf; for(int j=1;j<=k;j++) KMP(i,j); //第i小的數j,看每位是否能通過這個數得到 for(int j=1;j<=n;j++) if(maxx[j]>=maxy[j]) maxx[j]=inf;else maxx[j]=maxy[j]; } for(int i=1;i<=n;i++) if(maxx[i]!=inf) ans++; printf("%d\n",ans); for(int i=1;i<=n;i++) if(maxx[i]!=inf) printf("%d\n",i); } return 0; }