【POJ3176】Cow Patterns KMP+树状数组(让苍天知道我不认输)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_40307434/article/details/78635423

题目大意:
根据一个子串的大小顺序找到原数组中满足条件的子串所有开头坐标并输出。

思路:
建立动态的树状数组存好ran1,ran2并利用ran1,ran2判断是否元素价值相同(在排序中处于相同位置)。如果不处于相同位置要将前面多余的元素删除,才能得到现在这个比较的子串的元素的真实价值。

改了很久很久主要还是之前没理清next数组。
next数组可能还是next[0]=next[1]=0的比较容易理解吧。
顺便把所有数组下标都取成1开头比较好看。

错误点注意!!:即使知道ran1[j+1]超出了K,这个j的坐标也!!不能在循环外动,因为while循环中我们要根据j-next[j]的改变删掉这么多个元素。


版本一:重头再来美滋滋一次WA一次过

/*
    poj 3167 kmp+树状数组
    time limit :2000 ms
    AC time: 469ms
    Memory 1640kB
*/

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;

int N,K,S;
int A[100100],B[25050];
int Next[25050],ran1[25050],ran2[25050];
vector <int> output;
void GetNext();
void Work();

int tree[30];
void change(int x,int p);
int getsum(int x);

int main()
{
    int i,j;
    scanf("%d%d%d",&N,&K,&S);
    for(i=1;i<=N;i++)
        scanf("%d",&A[i]);
    memset(tree,0,sizeof(tree));
    for(j=1;j<=K;j++)
    {
        scanf("%d",&B[j]);
        change(B[j],1);
        ran1[j]=getsum(B[j]-1);
        ran2[j]=getsum(B[j]);
    }
    GetNext();
    Work();
    return 0;
}

void change(int x,int p)
{
    while(x<=S)
    {
        tree[x]+=p;
        x+=x&(-x);
    }
}

int getsum(int x)
{
    int sum=0;
    while(x>0)
    {
        sum+=tree[x];
        x-=x&(-x);
    }
    return sum;
}

void GetNext()
{
    //cout<<"here"<<endl;
    int i,j;
    memset(tree,0,sizeof(tree));
    memset(Next,0,sizeof(Next));
    j=0;
    Next[0]=0;
    Next[1]=0;
    int cutindex=2;
    for(i=2;i<=K;i++)
    {
        change(B[i],1);
        int r1=getsum(B[i]-1);
        int r2=getsum(B[i]);
        while(j&&!(ran1[j+1]==r1&&ran2[j+1]==r2))
        {
            int cuttot=j-Next[j];
            j=Next[j];
            while(cuttot--)
                change(B[cutindex++],-1);
            r1=getsum(B[i]-1);
            r2=getsum(B[i]);
        }
        if(ran1[j+1]==r1&&ran2[j+1]==r2)
            j++;
        Next[i]=j;
    }
}

void Work()
{
    int i,j=0;
    memset(tree,0,sizeof(tree));
    int cutindex=1;
    for(i=1;i<=N;i++)
    {
        change(A[i],1);
        int r1=getsum(A[i]-1);
        int r2=getsum(A[i]);
        while(j&&!(ran1[j+1]==r1&&ran2[j+1]==r2))
        {
            int cuttot=j-Next[j];
            j=Next[j];
            while(cuttot--)
                change(A[cutindex++],-1);
            r1=getsum(A[i]-1);
            r2=getsum(A[i]);
        }
        if(ran1[j+1]==r1&&ran2[j+1]==r2)
        {
            j++;
            if(j==K)
                {
                    output.push_back(i-j+1);
                }
        }
    }
    cout<<output.size()<<endl;
    for(i=0;i<output.size();i++)
    {
        cout<<output[i]<<endl;
    }
}

版本二:昨天因为上面的错误点改了一晚上没搞出来情绪崩溃今天才A掉哒版本

/*
poj 3167 kmp+树状数组
time limit :2000 ms
AC time : 547ms
Memory : 1668kB
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
const int NMAX=1e5+70;
const int KMAX=3e4+7;

int N,K,S;
int Next[KMAX],ran1[KMAX],ran2[KMAX];
int a[NMAX],b[KMAX];
int t[30];
vector <int> out;
void change(int x,int p);
int sum(int k);
void get_next();
void work();

int main()
{
    int i,j;
    while(scanf("%d%d%d",&N,&K,&S)!=EOF)
    {
        out.clear();
        for(i=1; i<=N; i++)
        {
            scanf("%d",&a[i]);
        }
        memset(t,sizeof(t),0);
        memset(ran1,sizeof(ran1),0);
        memset(ran2,sizeof(ran2),0);
        for(j=1; j<=K; j++)
        {
            scanf("%d",&b[j]);
            change(b[j],1);
            ran1[j]=sum(b[j]-1);
            ran2[j]=sum(b[j]);
        }
        get_next();
        work();
    }
    return 0;
}


void change(int x,int p)//将第x个数加p
{
    while(x<=S)
    {
        t[x]+=p;
        x+=x&(-x);
    }
}

int sum(int k)//前k个数的和
{
    int ans=0;
    while(k>0)
    {
        ans+=t[k];
        k-=k&(-k);
    }
    return ans;
}


void get_next()
{
    int i,j,cut=2;
    int val1,val2;
    memset(Next,0,sizeof(Next));
    memset(t,0,sizeof(t));
    Next[1]=0;
    j=0;
    for(i=2; i<=K; i++)
    {
        change(b[i],1);
        val1=sum(b[i]-1);
        val2=sum(b[i]);
        while(j!=0&&(!(ran1[j+1]==val1&&ran2[j+1]==val2)))
        {
            int tot=j-Next[j];
            while(tot--)
            {
                change(b[cut++],-1);
            }
            j=Next[j];
            val1=sum(b[i]-1);
            val2=sum(b[i]);
        }
        if(ran1[j+1]==val1&&ran2[j+1]==val2)
        {
            ++j;
        }
        Next[i]=j;
    }
}

void work()
{

    int i,j=0,cut=1;
    int val1,val2;
    memset(t,0,sizeof(t));
    for(i=1; i<=N; i++)
    {
        change(a[i],1);
        val1=sum(a[i]-1);
        val2=sum(a[i]);
        //cout<<a[i]<<' '<<val1<<' '<<val2<<endl;
        while(j!=0&&(!(ran1[j+1]==val1&&ran2[j+1]==val2)))
        {
            int tot=j-Next[j];
            j=Next[j];
            while(tot--)
                change(a[cut++],-1);
            val1=sum(a[i]-1);
            val2=sum(a[i]);
        }
        if(ran1[j+1]==val1&&ran2[j+1]==val2)
            ++j;
        if(j==K)
        {
            out.push_back(i-j+1);
        }
    }
    cout<<out.size()<<endl;
    for(i=0; i<out.size(); i++)
    {
        cout<<out[i]<<endl;
    }
}

真的人笨起来会很可怕的。
唉周五的东西到周六晚上才完全弄懂。
我都嫌弃自己。
所以就文末悄咪咪:
比个大大的心给最近大作业赶到天昏地暗依然挤出时间远程陪我死磕代码的最聪明最耐心的吴老师!基友一生一起走!!~~

猜你喜欢

转载自blog.csdn.net/qq_40307434/article/details/78635423