字符串匹配小结

字符串匹配小结

声明

文章均为本人技术笔记,转载请注明出处:
[1] https://segmentfault.com/u/yzwall
[2] blog.csdn.net/j_dark/

字符串匹配问题

问题描述:对于一个给定的 source 字符串和一个target字符串,你应该在source字符串中找出 target 字符串出现的第一个位置(从0开始)。如果不存在,则返回 -1。

source串长度设为 n target串长度设为m

1 O(nm) 时间复杂度:暴力匹配

  • lintcode strstr,该题目不要求时间复杂度优化到 O(nm)
class Solution {
    public int strStr(String source, String target) {
        if (source == null || target == null) {
            return -1;
        }
        int n = source.length();
        int m = target.length();
        if (m == 0) {
            return 0;
        }
        int i, j;
        for (i = 0; i <= n - m; i++) {
            for (j = 0; j < m; j++) {
                if (source.charAt(i + j) != target.charAt(j)) {
                    break;
                }
            }
            if (j == m) {
                return i;
            }
        }
        return -1;
    }
}

2 O(n+m) 复杂度:Rarbin-Karp算法

2.1 Rarbin-Karp算法思想

暴力匹配的时间开销 O(m) 在于需要检查以source串每个字符开头的子串是否匹配,Rabin-Karp算法的思想是用尽量一一对应的散列值去代表每个字符串,有推论:
+ 推论一:hashcode不相等的字符串字面值必然不同;
+ 推论二:hashcode相同的字符串,因为哈希冲突无法避免,可能字面值不同,需要继续进行一一字符检查;
优化效果:通过计算字符串的hashcode,将字符串字符挨个比较的时间开销O(m)优化到比较hashcode的时间开销 O(1) ;不过由于计算需要计算散列值,在匹配前需要进行预处理;

2.2 实现注意点说明

在实际运算中,极容易发生整数溢出bug,因此需要不断进行模运算,选取任意大数取模时应注意不易过大

2.2 代码实现

散列函数参考java.lang.String类,假定有字符串target,字面值为 sm1sm2..s1s0 长度为 m ,target串的散列值计算公式如下:

hash(target)=sm1×31m1+sm2times31m2+...+s1×311+s0×310

实现步骤:
1. 预处理:计算target串的散列值targetCode 31m1
2. 扫描source串:维护扫描窗口大小为 m ,计算窗口字串的散列值hashcode,并与targetCode相比较
如果二者不相等,根据推论一,扫描窗口向后移动一个字符,新的hashcode需要减去刚被移出扫描窗口的上一个字符s的权值 s×31m1
如果二者相等,继续比较字面值是否相同;

public class Solution {
    public int strStr2(String source, String target) {
        if (source == null || target == null) {
            return -1;
        }
        int n = source.length();
        int m = target.length();
        if (m == 0) {
            return 0;
        }

        int mod = 1000000;
        int targetCode = 0;
        int power = 1;
        for (int i = 0; i < m; i++) {
            targetCode = (targetCode * 31 + target.charAt(i)) % mod;
            power = (power * 31) % mod; 
        }

        int hashCode = 0;
        for (int i = 0; i < n; i++) {
            hashCode = (hashCode * 31 + source.charAt(i)) % mod;
            if (i < m - 1) {
                continue;
            }
            if (i >= m) {
                hashCode = (hashCode - source.charAt(i - m) * power) % mod;
                // 防止负数溢出
                if (hashCode < 0) {
                    hashCode += mod;
                }
            }
            if (hashCode == targetCode) {
                if (source.substring(i - m + 1, i + 1).equals(target)) {
                    return i - m + 1;
                }
            }
        }
        return -1;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值