题解 P8085 [COCI2011-2012#4] KRIPTOGRAM
思路并不是很直接的一道哈希题,并不算特别难,但也不简单。
分析
看到题意后有字符串匹配,很容易想到哈希。
因为每一个明文对应着一个密文,可以记当前单词距离到上一个相同单词的距离(如果没有即为
为了快速判断这样一个单词距离序列相同,可以用哈希。
滑动查询区间的时候,需要移除新区间中被移除单词第一个位置的哈希值,这个用队列存一下就行。
时间复杂度
注意事项
因为使用了队列 std::queue
,而默认的实现方式是 std::queue<,std::deque<> >
,空间会爆炸,所以我们应该使用 std::queue<,std::list<> >
。
代码
//the code is from chenjh
#include<cstdio>
#include<cstring>
#include<iostream>
#include<list>
#include<queue>
#include<unordered_map>
#define MAXN 1000005
using namespace std;
typedef unsigned long long ULL;
const unsigned int p=131;
ULL pn[MAXN];
int n,m,t=0;
unordered_map<string,int> M;
int a[MAXN],b[MAXN],c[MAXN];//c[i] 表示第 i 个单词所造成的哈希贡献。
int ls[MAXN];//记录上一个单词出现的位置。
queue<int,list<int> > Q[MAXN];//存储每一个单词的位置队列信息。
int main(){
for(string s;cin>>s&&s!="$";){
if(M.find(s)==M.end()) M[s]=++t;
a[++n]=M[s];
}//将 a 的单词转化为数字,方便比较。
pn[0]=1;
for(int i=1;i<=n;i++) pn[i]=pn[i-1]*p;//预处理 p 的幂次方数组。
M.clear(),t=0;//注意清空重新分配编号。
for(string s;cin>>s&&s!="$";){
if(M.find(s)==M.end()) M[s]=++t;
b[++m]=M[s];
}//将 b 的单词转化为数字,方便比较。
ULL hsa=0,hsb=0;//a 的区间哈希值和 b 的哈希值。
for(int i=1;i<=m;i++) hsb=hsb*p+(ls[b[i]]?i-ls[b[i]]:0),ls[b[i]]=i;//相减得到距离,找不到上一个则为 0.
memset(ls,0,sizeof ls);
for(int i=1;i<=m;i++){
c[i]=ls[a[i]]?i-ls[a[i]]:0;
hsa=hsa*p+c[i];
Q[a[i]].push(ls[a[i]]=i);
}
if(hsa==hsb) return puts("1"),0;
for(int i=m+1;i<=n;i++){
Q[a[i-m]].pop();//弹出最前端单词。
if(!Q[a[i-m]].empty()) hsa-=c[Q[a[i-m]].front()]*pn[i-Q[a[i-m]].front()-1];//删除新的队列中第一个被删除单词所贡献的哈希值,如果不存在则不需要删除。
c[i]=(ls[a[i]]>i-m)?i-ls[a[i]]:0;
hsa=hsa*p+c[i];//加入最新元素。
Q[a[i]].push(ls[a[i]]=i);//加入新的单词。
if(hsa==hsb) return printf("%d\n",i-m+1),0;
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】