题目描述
字符串的循环同构:表示把字符串的左边第一位移到最后一位,新串再进行这样的操作,得到的一些字符串都是原串的循环同构,例如"bcda"、"cdab"、"dabc"都是"abcd"的循环同构。
给你两个长度相等的字符串 aaa 和 bbb ,字符串 aaa 和 bbb 均由小写字母组成。
当某个字符 xxx 在字符串 aaa 与字符串 bbb 或 bbb 的循环同构中出现的所有位置依次对应时(对于字符串的每一位,要么 aaa 和 bbb 的这一位都是该字符,要么都不为该字符),称该字符为“好”字符。例如:当 aaa = "acba",bbb = "bdaa"时,字符'a'与字符'b'为“好”字符
现在,我们想请你判断字符串 aaa 与字符串 bbb 中“好”字符的数量
输入描述:
第一行给你一个 nnn (1≤n≤106),n(1 \leq n \leq 10^6),n(1≤n≤106),n 为字符串 aaa 与 bbb 的长度。 第二行与第三行为字符串 aaa 和 bbb ,aaa 和 bbb 均由小写字母组成。
输出描述:
单行输出一个数字,表示“好”字符的个数。
示例1
输入
复制6 acabxb eababf
6 acabxb eababf
输出
复制2
2
说明
当b的循环同构体为"ababfe"时,字符'a'为“好”字符,当b的循环同构体为"feabab"时,字符'b'为“好”字符。
示例2
输入
复制3 abc cde
3 abc cde
输出
复制1
1
做法
本题的有两个关键。一是这个“循环同构”,在原字符串后面再复制一份原字符串,然后新的字符串中子串长度为n的子串就是原字符串的循环同构。二是分26个字母求哈希值,就是枚举字母,对于新串中是这个字母则值为‘1’,不是这个字母的话就是‘0’(别的也行)。然后遍历新字符串,找长度为n的区间,看他的哈希值和b串的哈希值是否相同,相同就ans++,然后找下一个可能的单词。
#include<bits/stdc++.h>
using namespace std;
const int P=131;
int n;
string a,b;
long long hxc[10000010],hxd[10000010],p[10000010];//hxc得是1e6的两倍(复制了)
map<char,int> mpa,mpb,mp,mp3;
int ans;
int main(){
scanf("%d",&n);
cin>>a>>b;
for(int i=0;i<n;i++){
mpa[a[i]]++;
mpb[b[i]]++;
}
for(int i=0;i<26;i++){//不是答案的字母
if(mpa[i+'a']==0&&mpb[i+'a']==0) mp[i+'a']++;
if(mpa[i+'a']!=mpb[i+'a']) mp[i+'a']++;
}
for(int i=0;i<26;i++){//是答案的字母
if(mp[i+'a']==0) mp3[i+'a']++;
}
a=a+a;
p[0]=1;
for(int i=1;i<=2*n;i++) {
p[i]=p[i-1]*P;
}
for(map<char,int>::iterator it=mp3.begin();it!=mp3.end();it++){
string c,d;
for(int i=0;i<a.size();i++){//算a串的哈希值
if(a[i]==it->first) c+='1';
else c+='0';
hxc[i+1]=hxc[i]*P+c[i];
}
for(int i=0;i<b.size();i++){//算b串的哈希值
if(b[i]==it->first) d+='1';
else d+='0';
hxd[i+1]=hxd[i]*P+d[i];
}
for(int i=n;i<=a.size();i++){//区间哈希
long long cntc;
cntc=hxc[i]-hxc[i-n]*p[n];
if(cntc==hxd[b.size()]){
ans++;
break;
}
}
}
cout<<ans;
}