点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
const int N=3e5+10,P=131;
ULL h[N],p[N];
char str[N];
//记录符合要求的字串的起始位置和长度
int res,st;
ULL get(int l,int r)
{
return h[r]-h[l-1]*p[r-l+1];
}
int main()
{
scanf("%s",str+1);
int len=strlen(str+1);
//预处理前缀和数组以及幂次
p[0]=1;
for(int i=1;i<=len;i++){
h[i]=h[i-1]*P+str[i];
p[i]=p[i-1]*P;
}
int l=0,r=len+1;
while(l<=r){
int mid=l+r>>1;
//建立一个图,从哈希前缀映射出现次数和位置
//一个循环统计字符串中长度为mid的字串的个数和起始位置(起始位置只记录第一个出现的即可)
unordered_map<ULL,int> cnt,pos;
for(int i=1;i+mid-1<=len;i++){
ULL hs=get(i,i+mid-1);
cnt[hs]++;
if(!pos.count(hs)) pos[hs]=i;
}
//然后去看仅出现一次的
int p=len+1;
bool ok=false;
for(auto &[hs,c] : cnt){
if(c==1){
if(pos[hs]<p) p=pos[hs],ok=true;
}
}
//真正的二分函数,上面相当于实现了一个check
if(ok){
res=mid,st=p;
r=mid-1;
}else l=mid+1;
}
for(int i=st;i<st+res;i++) putchar(str[i]);
return 0;
}
哈希映射表,查找是否存在最小不重复字符串
最小不重复,那就是检查从小到大的所有字串
我学会了一种方法for(int i=1;i+mid-1<=n;i++)这就能遍历长度一定的字符串中的所有这个长度的字串了
以及哈希表可以实现一个hs映射多个位置,如本题就开了两个map,用一个hs映射start和cnt
以及二分查找函数的新写法,核心是先判断单调性,如果check函数不是很好写的话,就在二分函数中执行函数,用一个flag判断,同时,由于不是直接返回二分的线段长度,l=r时也要进行处理,故最后条件应该是l<=r