NCPC 2014 H clock pictures

题目链接:

  http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1794


题目是问两个钟能否重合,考虑将数据读入后排序,之后作差产生新的序列(注意作差的时候要作N个,即最后一个与第一个之间的差也要做上,否则会WA)

产生的两个差序列进行匹配就可以,有两种方法,首先想到的是循环KMP,将一个序列复制一遍用另一个序列进行匹配就可以。

代码如下:

(这道题在做的过程中还出现了一些小问题,对于OJ上面读入使用while(scanf()!=EOF)或者while(~scanf()),而使用while(scanf())有可能会造成TLE)


#include<iostream>
#include<cstdio>
#include<cstring>
#include"algorithm"
using namespace std;
const int maxn=400000+10;
int numa[maxn],numb[maxn];
int fail[maxn],a[maxn],b[maxn];
int n;
const int modd=360000;

void getfail()
{
    memset(fail,0,sizeof(fail));fail[0]=-1;
    for(int i=1;i<n;i++)
    {
        int p=fail[i-1];
        while(p>=0&&a[p+1]!=a[i])p=fail[p];
        fail[i]=p+1;
    }
}

int main()
{
    while(~scanf("%d",&n))
    {
        int flag=0;
        
        for(int i=0;i<n;i++)
        {
            
            scanf("%d",&numa[i]);
           
        }
          for(int i=0;i<n;i++)
        {
            
            scanf("%d",&numb[i]);
           
        }
        sort(numa,numa+n);
        sort(numb,numb+n);
        numa[n]=numa[0];
        numb[n]=numb[0];
        for(int i=1;i<=n;i++)
        {
            a[i]=(numa[i]-numa[i-1]+modd)%modd;
            b[i]=(numb[i]-numb[i-1]+modd)%modd;

        }


        getfail();
        for(int i=n+1;i<=2*n;i++)
            b[i]=b[i-n];
        for(int i=1,p=0;i<=n*2;i++)
        {
            while(p>=0&&a[p+1]!=b[i])p=fail[p];
            if(++p==n){flag=1; break;}
        }
        if(flag) cout<<"possible"<<endl;
        else cout<<"impossible"<<endl;
    }
}

另一种方法是参考大神的博客学习的,如果两个字符串能够匹配(在这个题目中应该叫做循环同构),那么他们的最小表示法是相同的,这是个充要条件。关于同样题目的解释,我找到了一个比较清楚的博客

那么我们只需要判断两个复制后的字符串的最小表示法是否相同就可以了

具体代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include"algorithm"
using namespace std;
const int maxn=400000+10;
int numa[maxn],numb[maxn];
int fail[maxn],a[maxn],b[maxn];
int n;
const int modd=360000;


int main()
{
    while(~scanf("%d",&n))
    {

        for(int i=0;i<n;i++)
        {
            
            scanf("%d",&numa[i]);
          
        }
          for(int i=0;i<n;i++)
        {
            
            scanf("%d",&numb[i]);
         
        }
        sort(numa,numa+n);
        sort(numb,numb+n);
        numa[n]=numa[0];
        numb[n]=numb[0];
        for(int i=1;i<=n;i++)
        {
            a[i+n]=a[i]=(numa[i]-numa[i-1]+modd)%modd;
            b[i+n]=b[i]=(numb[i]-numb[i-1]+modd)%modd;

        }


        int i,j,k;
        for(i=1,j=1;i<=n&&j<=n;)
        {
            for(k=0;k<n;k++)
            {
                if(a[i+k]>b[j+k])
                {
                    i+=k+1;
                    break;
                }
                else if(a[i+k]<b[j+k])
                {
                    j+=k+1;
                    break;
                }

            }
            if(k==n) break;
        }
        if(k==n) cout<<"possible"<<endl;
        else cout<<"impossible"<<endl;
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值