2010-1140

从 Trac 迁移的文章

这是从旧校内 Wiki 迁移的文章,可能存在一些样式问题,您可以向 memset0 反馈。

原文章内容如下:

题意大致为:有一种加密方法,对于字符串P,首先将字符串中的字符进行替换,然后可以对位置进行交换,最后将它插入另一个字符串中间获得加密串T。然后对于一对T与P,求T是否可以为P的一个加密结果,若是则求所有的加密位置。

因为所有的字符可以替换,位置也可以改变,因此两个字符串只要字符重复的不同次数相同就可以通过变换得到。因此对于T与P可以分别用一个数组cnt记录字符的出现次数。将T从头到尾扫一次,只要该数组排序后的结果相同,就说明这是一个可能的插入位置。

但是每次排序的开销太大会TLE。可以考虑用另一个大小为500000的数组fcnt记录出现次数相同的不同字符数,扫描时实时更新这两个数组。虽然该数组有500000,但判断是否相同时并不需要每个元素比较,因为其中可能的值只会在cnt中出现,其它的值必然全部为0。因此每次只要比较fcnt[cnt[i]]的值即可

代码如下:
{{{
#!cpp
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
char t[500010],p[500010];
int cnt[128],cnp[128];
int fcnt[500010],fcnp[500010];
bool first,good;
int delta;
void output(int a)
{
    if (first)
    {
        printf("Yes\n");
        printf("%d",a);
        first=false;
    }else
    printf(" %d",a);
}
int main()
{
    int i,j,k,pk;
    while (scanf("%s",t)!=EOF)
    {
        first=true; good=false;
        memset(cnt,0,sizeof(cnt));
        memset(cnp,0,sizeof(cnp));

        memset(fcnt,0,sizeof(fcnt));
        memset(fcnp,0,sizeof(fcnp));
        scanf("%s",p);

        for (i=0;p[i]>0;i++)
            cnp[(int)p[i]]++;
        pk=i;
        for (i=0;i<128;i++)
            fcnp[cnp[i]]++;
        for (i=0;i<pk && t[i]>0;i++)
            cnt[(int)t[i]]++;
        if (t[i]==0) 
        {
            printf("No\n");
            continue;
        }
        for (i=0;i<128;i++)
        {
            fcnt[cnt[i]]++;
        }
        for (i=0;i<128;i++)
        {
            k=cnt[i];
            if (fcnt[k]!=fcnp[k]) break;
        }

        if (i==128) output(0);
        for (i=pk;t[i]>0;i++)
        {
            fcnt[cnt[(int)t[i]]]--;
            cnt[(int)t[i]]++;
            fcnt[cnt[(int)t[i]]]++;

            fcnt[cnt[(int)t[i-pk]]]--;
            cnt[(int)t[i-pk]]--;
            fcnt[cnt[(int)t[i-pk]]]++;

            for (j=0;j<128;j++)
            {
                k=cnt[j];
                if (fcnt[k]!=fcnp[k]) break;
            }
            if (j==128) output(i-pk+1);
        }
        if (first)
        {
            printf("No\n");
        }else printf("\n");
    }
    return 0;
}           
}}}
by pxhdg

题意大致为:有一种加密方法,对于字符串P,首先将字符串中的字符进行替换,然后可以对位置进行交换,最后将它插入另一个字符串中间获得加密串T。然后对于一对T与P,求T是否可以为P的一个加密结果,若是则求所有的加密位置。

因为所有的字符可以替换,位置也可以改变,因此两个字符串只要字符重复的不同次数相同就可以通过变换得到。因此对于T与P可以分别用一个数组cnt记录字符的出现次数。将T从头到尾扫一次,只要该数组排序后的结果相同,就说明这是一个可能的插入位置。

但是每次排序的开销太大会TLE。可以考虑用另一个大小为500000的数组fcnt记录出现次数相同的不同字符数,扫描时实时更新这两个数组。虽然该数组有500000,但判断是否相同时并不需要每个元素比较,因为其中可能的值只会在cnt中出现,其它的值必然全部为0。因此每次只要比较fcnt[cnt[i]]的值即可

代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
char t[500010],p[500010];
int cnt[128],cnp[128];
int fcnt[500010],fcnp[500010];
bool first,good;
int delta;
void output(int a)
{
    if (first)
    {
        printf("Yes\n");
        printf("%d",a);
        first=false;
    }else
    printf(" %d",a);
}
int main()
{
    int i,j,k,pk;
    while (scanf("%s",t)!=EOF)
    {
        first=true; good=false;
        memset(cnt,0,sizeof(cnt));
        memset(cnp,0,sizeof(cnp));
        memset(fcnt,0,sizeof(fcnt));
        memset(fcnp,0,sizeof(fcnp));
        scanf("%s",p);
        for (i=0;p[i]>0;i++)
            cnp[(int)p[i]]++;
        pk=i;
        for (i=0;i<128;i++)
            fcnp[cnp[i]]++;
        for (i=0;i<pk && t[i]>0;i++)
            cnt[(int)t[i]]++;
        if (t[i]==0) 
        {
            printf("No\n");
            continue;
        }
        for (i=0;i<128;i++)
        {
            fcnt[cnt[i]]++;
        }
        for (i=0;i<128;i++)
        {
            k=cnt[i];
            if (fcnt[k]!=fcnp[k]) break;
        }
        if (i==128) output(0);
        for (i=pk;t[i]>0;i++)
        {
            fcnt[cnt[(int)t[i]]]--;
            cnt[(int)t[i]]++;
            fcnt[cnt[(int)t[i]]]++;
            fcnt[cnt[(int)t[i-pk]]]--;
            cnt[(int)t[i-pk]]--;
            fcnt[cnt[(int)t[i-pk]]]++;
            for (j=0;j<128;j++)
            {
                k=cnt[j];
                if (fcnt[k]!=fcnp[k]) break;
            }
            if (j==128) output(i-pk+1);
        }
        if (first)
        {
            printf("No\n");
        }else printf("\n");
    }
    return 0;
}           

by pxhdg