LeetCode438-找到字符串中所有字母异位词

题目链接

LeetCode438


滑动窗口,相比应该不用多说了,就是一个抽象的窗口,每次验证窗口内的值。

但是本题其实也可以不算滑动窗口,因为这个窗口是定长的,相当于每次的移动方式是已知的。

就变成了一个定长滑动字符串匹配问题。


先写个暴力,超时了

每次对窗口内的字符串排序,然后比较:

var q []byte
func findAnagrams(s string, p string) []int {
    
    ans:=make([]int,0)
    if len(s)<len(p){
        return ans
    }
    l,r:=0,len(p)-1
    tmp:=[]byte(s[:len(p)])
    ss:=strings.Split(p,"")
    sort.Strings(ss)
    p=strings.Join(ss,"")
    for r<len(s){
        if check(tmp,p){
            ans=append(ans,l)
        }
        r++
        l++
        if r<len(s){
            tmp=append(tmp,s[r])
        }
        
        tmp=tmp[1:]
    }
    return ans
}

func check(a []byte,o string)bool{
    //fmt.Println(string(a),o)
    q=make([]byte,len(a))
    copy(q,a)
    sort.Sort(sortRunes(q))    
    return string(q)==o
}

type sortRunes []byte

func (s sortRunes) Less(i, j int) bool {
    return s[i] < s[j]
}

func (s sortRunes) Swap(i, j int) {
    s[i], s[j] = s[j], s[i]
}

func (s sortRunes) Len() int {
    return len(s)
}

没啥悬念,超时了。怎么优化呢
其实按照以往思路,也可以写个统计数组,来记录每个字符出现的次数。


方法一:记录字符出现次数

官姐有写,算是比较常规的方法,不展开了。


方法二: 字符串哈希

哈希这个东西,就很神奇,可以把一个字符串映射成一个数,这样子滑动窗口验证的时候,就只需要比较两次。
但是普通哈希,不同顺序的值是不一样的呀,比如,"abc"的哈希值一定要和"cba"不一样,这样有和题目要求冲突了,怎么办呢?

我们可以自定义哈希函数,这里笔者用了双哈希,其实单哈希也可以,主要是自定义哈希函数比较重要。
我定义:
hash函数 f 1 ( s ) = ( ∑ i 0 → l e n ( n o w S t r ) − 1 i n t 64 ( n o w S t r [ i ] ) 2 ) m o d   M o d 1 f1(s)=(\displaystyle \sum^{}_{i 0\to len(nowStr)-1}{int64(nowStr[i])^2})mod\:Mod1 f1(s)=(i0len(nowStr)1int64(nowStr[i])2)modMod1
hash函数 f 2 ( s ) = ( ∑ i 0 → l e n ( n o w S t r ) − 1 i n t 64 ( n o w S t r [ i ] ) 3 ) m o d   M o d 2 f2(s)=(\displaystyle \sum^{}_{i 0\to len(nowStr)-1}{int64(nowStr[i])^3})mod\:Mod2 f2(s)=(i0len(nowStr)1int64(nowStr[i])3)modMod2
其实就是各位数的平方和各位数的立方和Mod1Mod2是两个大整数,用来减少哈希冲突的。

var q []byte
var mod1,mod2 int64
func findAnagrams(s string, p string) []int {
    mod1=1e15+7
    mod2=1e15+9
    ans:=make([]int,0)
    if len(s)<len(p){
        return ans
    }
    l,r:=0,len(p)-1
    now:=cal(s[:len(p)])
    pp:=cal(p)
    for r<len(s){
        if check(now,pp){
            ans=append(ans,l)
        }
        r++
        if r<len(s){
            now[0]+=int64(s[r])*int64(s[r])
            now[1]+=int64(s[r])*int64(s[r])*int64(s[r])
            now[0]%=mod1
            now[1]%=mod2
        }
        
        now[0]-=int64(s[l])*int64(s[l])
        now[1]-=int64(s[l])*int64(s[l])*int64(s[l])
        now[0]=(now[0]+mod1)%mod1
        now[1]=(now[1]+mod2)%mod2
        l++
    }
    return ans
}

func cal(s string)(ans []int64){
    a,b:=int64(0),int64(0)
    for i:=range s{
        a+=int64(s[i])*int64(s[i])
        b+=int64(s[i])*int64(s[i])*int64(s[i])
        a%=mod1
        b%=mod2
    }
    ans=append(ans,a,b)
    return
}

func check(a,b []int64)bool{
    return a[0]==b[0] && a[1]==b[1]
}

小小坑点

题目描述当中,没有规定len(s)≥len(p),所以需要进行特判。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值