题意
给定一个主串S,m个模式串T,问主串中有多少个字串与模式串T比配
这里的匹配指首尾字符相同,中间部分可以乱序(可以理解为相同字母个数相同)。
思路
整体思路就是,将T个模式串按照长度排序,相同长度的模式串一起处理。
对于每一种长度len,求一次主串S长度为len的子串哈希值。考虑到空间问题,我们只存储有贡献的子串,即出现过的。
赛时维护的位置前缀和应该是会冲突的,所以这里用哈希和来维护。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e4+5;
const int mod = 1e9+7;
ll tab[26]={34183,13513,152993,13591,19687,350869,111187,766091,769297,633469,752273,298651,617191,880421,136067,1408397,726899,458921,2133701,2599847,2730947,4696343,10267237,18941059,34078909,69208409};
int T,m,ans[maxn];
string s,t;
struct unt {
int len, id, l, r;
ll val;
}p[maxn];
bool cmp(unt a,unt b){
return a.len < b.len;
}
ll gethash(string s){
ll res = 0;
int sz = s.length();
for(int i=1;i<sz-1;i++) res += tab[s[i]-'a'];
return res;
}
unordered_map<ll,int> mp[26][26];
void solve(int len){
ll res = 0;
for(int i=1;i<len-1;i++) res += tab[s[i]-'a'];//calculate S
if(mp[s[0]-'a'][s[len-1]-'a'].count(res))//为了节省空间,只存储了t出现过的,即==0的
mp[s[0]-'a'][s[len-1]-'a'][res]++;
for(int i=len;i<s.length();i++){
res = res - tab[s[i-len+1]-'a'] + tab[s[i-1]-'a'];
if(mp[s[i-len+1]-'a'][s[i]-'a'].count(res))
mp[s[i-len+1]-'a'][s[i]-'a'][res]++;
}
}
void init(){
for(int i=0;i<26;i++){
for(int j=0;j<26;j++){
mp[i][j].clear();
}
}
}
int main(){
cin>>T;
while(T--){
init();
cin>>s;
cin>>m;
for(int i=0;i<m;i++){
cin>>t;
p[i]=(unt){(int)t.length(),i,t[0]-'a',t[t.length()-1]-'a',gethash(t)};
mp[p[i].l][p[i].r][p[i].val] = 0;
}
sort(p,p+m,cmp);
int pre = 0;
for(int i=0;i<m;i++){
if(p[i].len != pre) solve(p[i].len);
pre = p[i].len;
ans[p[i].id] = mp[p[i].l][p[i].r][p[i].val];
}
for(int i=0;i<m;i++) printf("%d\n",ans[i]);
}
return 0;
}