串的模式匹配
子串的定位运算通常称为串的模式匹配或串匹配,常见的算法有 BF、RK、KMP、Z
算法等。
设有两个字符串S
和T
,设 S
为主串;T
为子串(模式串)。在主串S
中查找与模式T
相匹配的子串,如果匹配成功,确定相匹配的子串中的第一个字符在主串S
中出现的位置。
BF 算法
最简单直观的模式匹配算法是BF(Brute-Force)算法,该算法依赖于纯粹的计算能力,尝试各种可能性。
例如,假设你有一个小挂锁,有4个数字,每个数字从0-9开始。你忘了密码,但你不想再买一把挂锁。由于您记不住任何数字,因此必须使用蛮力方法来打开锁。
所以你把所有的数字都设置回0,然后一个接一个地尝试:0001、0002、0003,以此类推,直到它打开。在最坏的情况下,需要10000次尝试才能找到你的组合。
BF算法的时间复杂度是 O (n * m)
。因此,如果我们使用蛮力在一个由m
字符组成的字符串中搜索一个由n
字符组成的字符串,那么需要n * m
次尝试。
算法步骤
-
设指针
i
和j
指示主串S
和模式T
中当前正待比较的字符位置,初始值设为 0。 -
若两个串均未比较到串尾,即
i
和j
均分别小于S
和T
的长度:S.ch[i]
和T.ch[j]
比较,若相等,则i
和j
分别指示串中下个位置,继续比较后续字符。- 若不等,指针后退重新开始匹配,从主串的下一个字符
(i = i - j + 1)
起再重新和模式的第一个字符(j = 1)
比较。
-
如果
j = T.length
,说明模式T
中的每个字符依次和主串S
中的一个连续的字符序列相等,匹配成功,返回和模式T
中第一个字符相等的字符在主串S
中的序号(i - T.length)
; -
否则称匹配不成功,返回 -1。
代码实现
/// <summary>
/// BF搜索
/// </summary>
public class BFSearch
{
/// <summary>
/// 搜索子串位置
/// </summary>
/// <param name="mainString">主串 S</param>
/// <param name="subString">子串 T</param>
/// <returns></returns>
public int SearchSubIndex(string mainString, string subString)
{
int i = 0, j = 0;
while (i < mainString.Length && j < subString.Length)
{
if (mainString[i] == subString[j])
{
i++;
j++;
}
else
{
i = (i - j) + 1;
j = 0;
}
}
if (j == subString.Length)
return i - subString.Length;
else
return -1;
}
}
Tests
[TestMethod]
public void BfTest()
{
var mainString1 = "abcd";
var subString1 = "cd";
Assert.IsTrue(_bFSearch.SearchSubIndex(mainString1, subString1) == 2);
var mainString2 = "aaabb";
var subString2 = "abc";
Assert.IsTrue(_bFSearch.SearchSubIndex(mainString2, subString2) == -1);
var mainString3 = "aaabaaaaab";
var subString3 = "baaaaab";
Assert.IsTrue(_bFSearch.SearchSubIndex(mainString3, subString3) == 3);
}
dotnet test
dotnet test --filter ClassName=Algorithm.Tests.BfTests
output
已通过! - 失败: 0,通过: 1,已跳过: 0,总计: 1,持续时间: 8 ms - Algorithm.Tests.dll (net7.0)