2015年7月18日 星期六

[HOJ 128] Matching

作法:

我們一樣往KMP的方向想,首先我們會需要 fail 函數,回顧他的定義,$fail[i]$代表說「當已經匹配好$1,...,i$時,如果$i+1$失配,那麼要轉換成已經匹配好$1,...,fail[i]$的情形」,而這裡的匹配的定義根一般字串匹配不一樣,是要兩個子序列的「相對大小關係」一樣時則叫作匹配。因此回顧我們在算 fail 函數時的方法,假設當前要決定$i$的 fail 值,令$fail[i-1]=j$,那麼首先我們要判斷「$1,...,i$的長度為$j+1$的後綴」是否和「長度$j+1$的前綴」匹配(再次強調這裡匹配的定義不一樣),而這等價於:「在$x[i-j],...,x[i-1]$之中比$x[i]$小的數的個數」等於「在$x[1],...,x[j]$中比$x[j+1]$小的數的個數」,其中$x$為要求$fail$函數的陣列。因此就可以維護一個 BIT 來查詢所求的答案(因為會支援加數字和刪數字)。如果發現失配了就一樣沿著 fail 往回跳,直到長度變$0$或是匹配成功為止。由這裡就可以發現,其實 fail 函數建立的過程幾乎一樣,只是變成要多維護兩個 BIT ,並用 BIT 來查詢是否匹配成功而已。在和原數列匹配的過程也一模一樣(本來在原本的寫法中建 fail 函數和匹配的過程的程式碼就幾乎一樣了),失配就沿 fail 往前跳,維護好兩個 BIT 即可。最後要記得當找到一個地方成功匹配了模版串時也要沿 fail 往前跳,跟原本的 KMP 一樣。


code :



#include<bits/stdc++.h>
#define lowbit(x) ((x)&(-x))
using namespace std;
const int maxn=1000000+10 ;
 
int bit[2][maxn] ;
void add(int id,int x,int val)
{
    for(;x<maxn;x+=lowbit(x)) bit[id][x]+=val ;
}
int query(int id,int x)
{
    int ret=0 ;
    for(;x;x-=lowbit(x)) ret+=bit[id][x] ;
    return ret ;
}
 
int a[maxn],b[maxn],fail[maxn] ;
vector<int> v,ans ;
main()
{
    int n,m ; scanf("%d%d",&m,&n) ;
    for(int i=1;i<=m;i++)
    {
        int x ; scanf("%d",&x) ;
        b[x]=i ;
    }
    for(int i=1;i<=n;i++) scanf("%d",&a[i]) , v.push_back(a[i]) ;
    sort(v.begin(),v.end()) ;
    for(int i=1;i<=n;i++) a[i]=lower_bound(v.begin(),v.end(),a[i])-v.begin()+1 ;
    for(int i=2,j=0;i<=m;i++)
    {
        while(j && query(0,b[j+1]-1)!=query(1,b[i]-1))
        {
            int j2=fail[j] ;
            for(;j!=j2;j--)
                add(0,b[j],-1) , add(1,b[i-j],-1) ;
        }
        if(query(0,b[j+1]-1)==query(1,b[i]-1))
            j++ , add(0,b[j],1) , add(1,b[i],1) ;
        fail[i]=j ;
    }
    memset(bit,0,sizeof(bit)) ;
    for(int i=1,j=0;i<=n;i++)
    {
        while(j && query(0,b[j+1]-1)!=query(1,a[i]-1))
        {
            int j2=fail[j] ;
            for(;j!=j2;j--)
                add(0,b[j],-1) , add(1,a[i-j],-1) ;
        }
        if(query(0,b[j+1]-1)==query(1,a[i]-1))
            j++ , add(0,b[j],1) , add(1,a[i],1) ;
        if(j==m)
        {
            ans.push_back(i-j+1) ;
            int j2=fail[j] ;
            for(;j!=j2;j--)
                add(0,b[j],-1) , add(1,a[i-j+1],-1) ;
        }
    }
    printf("%d\n",ans.size()) ;
    for(int i=0;i<ans.size();i++) printf("%d%c",ans[i],i+1==ans.size()?'\n':' ') ;
}
 

沒有留言:

張貼留言