[学习][HDU4300]字符串哈希 Clairewd's message

来源:互联网 发布:淘宝上怎么买到好衣服 编辑:程序博客网 时间:2024/06/05 23:54

字符串哈希的简介
对于一个字符串,如ababab,把它当做一个数处理,如31进制数,那么它就是hash[i]=a315+b314+a313+b312+a31+b(用该字符的ASCII码计算),可以直接用进位的方法实现hash[i]=hash[i1]31+s[i],而其中某一段区间[l,r]的hash值就为hash[r]hash[l1]xrl+1
但是如果字符串长度太长会溢出,于是我们把hash值储存在unsigned long long里面, 那样溢出时,会自动取余2的64次方,虽然这样可能会出现不同字符串有相同hash值的情况,但是概率极低。

字符串哈希的实现
其实在简介里都说的差不多了……就是记住给字符串取进制的时候31进制比较优秀,而如果是存下整个字符串的hash值到hash表中,取模的大质数可以用比较喜剧的23333(居然是质数?其实23、233、2333也是)。

例题
题目背景
HDU4300

题目描述
Clairewd is a member of FBI. After several years concealing in BUPT, she intercepted some important messages and she was preparing for sending it to ykwd. They had agreed that each letter of these messages would be transfered to another one according to a conversion table.
Unfortunately, GFW(someone’s name, not what you just think about) has detected their action. He also got their conversion table by some unknown methods before. Clairewd was so clever and vigilant that when she realized that somebody was monitoring their action, she just stopped transmitting messages.
But GFW knows that Clairewd would always firstly send the ciphertext and then plaintext(Note that they won’t overlap each other). But he doesn’t know how to separate the text because he has no idea about the whole message. However, he thinks that recovering the shortest possible text is not a hard task for you.
Now GFW will give you the intercepted text and the conversion table. You should help him work out this problem.

题目大意
看到GFW确实是想歪了orz(出题人搞事),就当他是人名吧……
你收到一个合成密文的表t(26个字母构成,第i个位置上字母表示字母表中第i个字母转换成这个字母,如t=qwertyuiopasdfghjklzxcvbnm,就相当于明文a转换成q,b转换成w,c转换成e…),又收到一篇文章,前一半是这篇文章的密文后一半是这篇文章的原文,但是原文有可能缺失了,比如说有一篇文章apple,它加密之后是elppa,那么你应该收到的是elppaapple,但是由于可能缺失了,所以你收到的可能是elppaapp,现在让你求出你收到的文章(密文+原文)可能的最短原始长度。

输入格式
第一行包含一个整数T,表示测试数据组数。
每组测试数据包含两行。每个测试案例的第一行是密码表。第二行n个字母表示那篇需要你恢复残缺的文章。可能文章本来就没有残缺。

测试数据的范围:
T<= 100 ;
n<= 100000;

输出格式
对于每组测试数据,输出一行,包含最短可能的完整文章。

样例数据
输入

2
abcdefghijklmnopqrstuvwxyz
abcdab
qwertyuiopasdfghjklzxcvbnm
qwertabcde

输出

abcdabcd
qwertabcde

备注
【样例说明】
对于第一组数据,因为密码表就是字母表,所以相当于没加密,原文密文一样,显然把第二个ab作为原文开始的地方时最短,这样恢复后就是abcdabcd。
对于第二组数据,因为a、b、c、d、e在密码表中对应的是q、w、e、r、t,和前面的qwert一一对应了,所以这是一篇没有残缺的文章,不需要恢复。

分析:由于数据比较小,可以用字符串哈希,假装整篇文章都是密文,全部翻译成原文再求hash1值,再把读入的文章当作原文原封不动求hash2值,直接枚举可能的文章(密文+原文)长度(也就是读入的文章的长度到读入的文章的长度的两倍之间),看这种长度下对应的保留的那部分原文的hash2和密文的hash1对的上不。

代码

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<string>#include<ctime>#include<cmath>#include<algorithm>#include<cctype>#include<iomanip>#include<queue>#include<set>using namespace std;int getint(){    int sum=0,f=1;    char ch;    for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());    if(ch=='-')    {        f=-1;        ch=getchar();    }    for(;isdigit(ch);ch=getchar())        sum=(sum<<3)+(sum<<1)+ch-48;    return sum*f;}const int N=100000+5;const unsigned long long jinzhi=31;//31进制unsigned long long hash1[N],hash2[N],mi[N];int T,len,ans,cov[30];char s[N],t[30],r[N];int main(){    freopen("message.in","r",stdin);    freopen("message.out","w",stdout);    mi[0]=1;    for(int i=1;i<=100000;++i)        mi[i]=mi[i-1]*jinzhi;    T=getint();    while(T--)    {        memset(cov,0,sizeof(cov));//清零操作        memset(t,0,sizeof(t));        memset(s,0,sizeof(s));        scanf("%s%s",t,s+1);        for(int i=0;i<26;++i)//因为最后要输出残缺部分的原文,所以不妨把密码表反过来将密文转原文,方便输出            cov[t[i]-'a']=i;        len=strlen(s+1),hash1[0]=0,hash2[0]=0;        for(int i=1;i<=len;++i)        {            hash1[i]=hash1[i-1]*jinzhi+(cov[s[i]-'a']);//把全文当成密文翻译成原文后hash            hash2[i]=hash2[i-1]*jinzhi+(s[i]-'a');//把全文当做原文hash        }        ans=len;        for(int i=len;i<=len*2;++i)//枚举长度        {            if(i&1) continue;            int tmp=i>>1;//密文长度            int lenm=len-tmp;//保留的那段原文长度lenm            unsigned long long s1=hash1[lenm];//密文前lenm长度的hash值            unsigned long long s2=hash2[len]-hash2[len-lenm]*mi[lenm];//保留的那段原文的hash值            if(s1==s2)//第一次发现是一样的就是最短答案了(因为长度是从小到大枚举的)            {                ans=tmp;                break;            }        }        for(int i=1;i<=ans;++i) printf("%c",s[i]);        for(int i=1;i<=ans;++i) printf("%c",cov[s[i]-'a']+'a');//直接按照密文翻译出原文        printf("\n");    }    return 0;}

本题结。

原创粉丝点击