T1
第一问开桶统计即可。
第二问我们采用双指针,不断地移动 \(r\) 直到包下含有最多单词数的区间,再移动 \(l\) 使答案更优并不断更新答案即可。
具体有一些细节见代码。时间复杂度 \(O(n \log n)\)。
可以把代码中的两个 map 换成数组存 hash value,时间可以降至 \(O(n)\),但是我懒了。()
code
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+5,M=1e5+5;
int n,m,ans1,ans2=1e9;
map<string,int> vis,cnt;
string a[N],b[M];
int main(){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i],vis[a[i]]=1;
cin>>m;
for(int i=1;i<=m;i++){
cin>>b[i];
if(vis[b[i]]==1) ans1++,vis[b[i]]++;
}
if(ans1==0) cout<<0<<'\n'<<0,exit(0);
int l=1,r=0,tot=0;
while(r<m){
while(tot<ans1&&r<m){
cnt[b[++r]]++;
if(cnt[b[r]]==1&&vis[b[r]]) tot++;
}
while(tot==ans1){
ans2=min(ans2,r-l+1);
cnt[b[l]]--;
if(!cnt[b[l]]&&vis[b[l]]) tot--;
l++;
}
}
cout<<ans1<<'\n'<<ans2;
return 0;
}
T2
很容易(?)发现一个性质:一个反对称字符串,去掉它两头的若干个字符,剩下的字符串仍是反对称的。
这启发我们不是去枚举子串,而是去枚举字串的对称轴。
其中对称轴一定位于两个字符之间,因为子串长度必定为偶数(是奇数则中间那个数会被取反,导致整个子串不可能反对称)。
接下来,我们二分子串长度的一半,然后比较子串的 hash value 与 它反对称后的 hash value 是否相等即可完成 check(这两个都可以预处理出来)。
然后对于每个反对称字串,根据我们发现的性质可得,它对答案的贡献即为 \(\frac{len}{2}\)(因为它两头去掉偶数个字符后仍为反对称字符串)。
code
#include<bits/stdc++.h>
#define ull unsigned long long
#define i64 long long
using namespace std;
const int N=5e5+5;
const int BASE=13331;
i64 n,ans;
string s;
ull hs[N],ht[N],p[N];
void init_hash(){
p[0]=1;
for(int i=1;i<=n;i++)
hs[i]=hs[i-1]*BASE+s[i],p[i]=p[i-1]*BASE;
for(int i=1;i<=n;i++) s[i]=(s[i]=='0'?'1':'0');
for(int i=n;i>=1;i--)
ht[i]=ht[i+1]*BASE+s[i];
}
int get_hs(int l,int r){
return hs[r]-hs[l-1]*p[r-l+1];
}
int get_ht(int l,int r){
return ht[l]-ht[r+1]*p[r-l+1];
}
bool check(int now,int x){
int ll=now-x/2+1,rr=now+x/2;
return get_hs(ll,rr)==get_ht(ll,rr);
}
int main(){
cin>>n>>s,s='#'+s;
init_hash();
for(int i=1;i<n;i++){
int l=0,r=min((i64)i,n-i)*2+1;
while(l+1<r){
int mid=(l+r)>>1;
if(check(i,mid)) l=mid;
else r=mid;
}
ans+=l/2;
}
cout<<ans;
return 0;
}
作业 T1
当然你可以用 map 水过,但是这里我们用 hash。
直接预处理每篇文章里每个单词的 hash value,然后对于每个询问,求出当前单词的 hash value 再一一比对即可。
map code
#include<bits/stdc++.h>
using namespace std;
int n,m,l;
map< string,set<int> > ms;
set<int>::iterator it;
int main(){
cin.tie(nullptr);
cout.tie(nullptr);
cin>>n;
for(int i=1;i<=n;i++){
cin>>l;
for(int k=1;k<=l;k++){ string tmp; cin>>tmp; ms[tmp].insert(i); }
}
cin>>m;
for(int i=1;i<=m;i++){
string tmp; cin>>tmp;
if(ms.count(tmp))
for(it=ms[tmp].begin();it!=ms[tmp].end();it++) cout<<*it<<' ';
cout<<'\n';
}
return 0;
}
hash code
#include<bits/stdc++.h>
#define ull unsigned long long
using namespace std;
const int N=5e3+5,M=1e3+5;
const int BASE=13331;
int n,m,l[N];
string s[N][M];
ull hs[N][M];
void init_hash(int i,int j){
for(int k=0;k<s[i][j].size();k++)
hs[i][j]=hs[i][j]*BASE+s[i][j][k];
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>l[i];
for(int j=1;j<=l[i];j++)
cin>>s[i][j],init_hash(i,j);
}
cin>>m;
for(int i=1;i<=m;i++){
string ss; cin>>ss;
ull ss_hash=0;
for(int j=0;j<ss.size();j++)
ss_hash=ss_hash*BASE+ss[j];
for(int j=1;j<=n;j++){
for(int k=1;k<=l[j];k++){
if(ss_hash==hs[j][k]){
cout<<j<<' ';
break;
}
}
}
cout<<'\n';
}
return 0;
}
作业 T2
二分最后的匹配成功的位置,截取 \(A,B\) 串相应区间比对完成 check,最后用桶统计答案即可。
具体细节见代码。
code
#include<bits/stdc++.h>
#define ull unsigned long long
using namespace std;
const int N=2e5+5;
const int BASE=13331;
int n,m,q,ans[N];
string a,b;
ull hsa[N],hsb[N],p[N];
void init_hash(){
p[0]=1;
for(int i=1;i<=n;i++) hsa[i]=hsa[i-1]*BASE+a[i],p[i]=p[i-1]*BASE;
for(int i=1;i<=m;i++) hsb[i]=hsb[i-1]*BASE+b[i];
}
ull get_hsa(int l,int r){
return hsa[r]-hsa[l-1]*p[r-l+1];
}
ull get_hsb(int l,int r){
return hsb[r]-hsb[l-1]*p[r-l+1];
}
int main(){
cin>>n>>m>>q>>a>>b;
a='#'+a,b='#'+b;
init_hash();
for(int i=1;i<=n;i++){
int l=0,r=min(n-i+1,m)+1;
while(l+1<r){
int mid=(l+r)>>1;
if(get_hsa(i,mid+i-1)==get_hsb(1,mid)) l=mid;
else r=mid;
}
ans[l]++;
}
while(q--){
int x; cin>>x,cout<<ans[x]<<'\n';
}
return 0;
}