字符串基础

字符串基础

字符串和数组有很多相似之处。比如同样使用 名称[下标] 的方式来访问一个字符。

之所以单独讨论字符串是因为:

  • 字符串中的数据元素都是字符,结构相对简单,但规模可能比较庞大。
  • 经常需要把字符串作为一个整体来使用和处理。操作对象一般不是某个数据元素,而是一组数据元素(整个字符串或子串)。
  • 经常需要考虑多个字符串之间的操作。比如:字符串之间的连接、比较操作。

根据字符串的特点,我们可以将字符串问题分为以下几种:

  • 字符串匹配问题。
  • 子串相关问题。
  • 前缀 / 后缀相关问题;
  • 回文串相关问题。
  • 子序列相关问题。

一、字符串的比较

字符串的大小取决于他们排列字符的前后顺序。这些比较是基于字符的 Unicode 值进行比较的。

在Java中,字符串的比较大小可以使用 compareTo 方法或 compareToIgnoreCase 方法。这两种方法都是在 java.lang.String 类中定义的。

compareTo 方法:

  • compareTo 方法用于按字典顺序比较两个字符串。它会将两个字符串进行逐字符比较,并返回一个整数值,指示两个字符串的大小关系。
  • 如果两个字符串相等,返回值为0。
  • 如果调用该方法的字符串在字典顺序上排在参数字符串之前,则返回值为负数。
  • 如果调用该方法的字符串在字典顺序上排在参数字符串之后,则返回值为正数。
String str1 = "hello";
String str2 = "world";
int result = str1.compareTo(str2);
// result 将会是一个负数,因为 "hello" 在 "world" 之前

compareToIgnoreCase 方法:

  • compareToIgnoreCase 方法与 compareTo 方法类似,但它会忽略字符串中的大小写差异。
  • 如果两个字符串相等(忽略大小写),返回值为0。
  • 如果调用该方法的字符串在字典顺序上排在参数字符串之前(忽略大小写),返回值为负数。
  • 如果调用该方法的字符串在字典顺序上排在参数字符串之后(忽略大小写),返回值为正数。

示例:

String str1 = "hello";
String str2 = "Hello";
int result = str1.compareToIgnoreCase(str2);
// result 将会是0,因为 "hello" 和 "Hello" 在忽略大小写的情况下相等

二、字符串的存储结构

在Java中,字符串的存储结构主要有两种:String 对象和字符数组(char array)。

2.1 String 对象:

  • Java 中的字符串是不可变的(immutable),即一旦创建,其内容不可更改。
  • String 对象内部使用字符数组来存储字符串的内容。
  • 字符串常量池(String Pool)是 Java 中用于存储字符串常量的特殊区域,String 对象的内容在创建时如果在字符串常量池中已存在相同内容的字符串,则不会创建新的对象,而是直接引用已存在的对象。
  • 使用 String 对象的优点是方便、安全,且可自动进行一些优化,但由于不可变性可能导致频繁的对象创建和销毁,对于大量字符串拼接或修改操作可能会影响性能。

2.2 字符数组(char array):

  • 可以使用字符数组来表示字符串,即 char[]
  • 字符数组中的每个元素存储一个字符,可以根据需要对数组中的元素进行修改。
  • 与 String 对象相比,字符数组的内容是可变的(mutable),因此适用于需要频繁修改字符串内容的场景。
  • 字符数组的缺点是需要手动管理字符串的长度和内存,操作相对繁琐,同时不具备 String 对象的一些便利特性,如字符串常量池和一些自动优化特性。
String str = "Hello World"; // 使用双引号包围字符串内容
String str = new String("Hello World"); // 使用构造方法传入字符串内容


char[] charArray = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd'};

2.3 转化关系

字符串和字符数组之间存在一定的相互转换关系,可以使用 toCharArray() 方法将字符串转换为字符数组,也可以使用 String 类的构造方法将字符数组转换为字符串。

将字符串转换为字符数组: 可以使用 toCharArray() 方法将字符串转换为字符数组。该方法会返回一个新的字符数组,其中包含了字符串中的所有字符。

示例:

String str = "Hello";
char[] charArray = str.toCharArray(); // 将字符串转换为字符数组

将字符数组转换为字符串: 可以使用String类的构造方法将字符数组转换为字符串。可以使用参数为字符数组的构造方法或者参数为字符数组的部分子数组的构造方法。

使用字符数组的构造方法:

char[] charArray = {'H', 'e', 'l', 'l', 'o'};
String str = new String(charArray); // 将字符数组转换为字符串

使用字符数组的部分子数组的构造方法:

javaCopy codechar[] charArray = {'H', 'e', 'l', 'l', 'o'};
String str = new String(charArray, 0, 3); // 将字符数组的前3个元素转换为字符串

三、字符串的匹配问题

字符串匹配(String Matching):又称模式匹配(Pattern Matching)。可以简单理解为,给定字符串 Tp,在主串 T 中寻找子串 p。主串 T 又被称为文本串,子串 p 又被称为模式串(Pattern)。

3.1 单模式匹配问题

  • 基于前缀搜索方法:在搜索窗口内从前向后(沿着文本的正向)逐个读入文本字符,搜索窗口中文本和模式串的最长公共前缀。
    • 著名的 Knuth-Morris-Pratt (KMP) 算法和更快的 Shift-Or 算法使用的就是这种方法。
  • 基于后缀搜索方法:在搜索窗口内从后向前(沿着文本的反向)逐个读入文本字符,搜索窗口中文本和模式串的最长公共后缀。使用这种搜索算法可以跳过一些文本字符,从而具有亚线性的平均时间复杂度。
    • 最著名的 Boyer-Moore 算法,以及 Horspool 算法、Sunday (Boyer-Moore 算法的简化) 算法都使用了这种方法。
  • 基于子串搜索方法:在搜索窗口内从后向前(沿着文本的反向)逐个读入文本字符,搜索满足「既是窗口中文本的后缀,也是模式串的子串」的最长字符串。与后缀搜索方法一样,使用这种搜索方法也具有亚线性的平均时间复杂度。这种方法的主要缺点在于需要识别模式串的所有子串,这是一个非常复杂的问题。
    • Rabin-Karp 算法Backward Dawg Matching (BDM) 算法、Backward Nondeterministtic Dawg Matching (BNDM) 算法和 Backward Oracle Matching (BOM) 算法使用的就是这种思想。其中,Rabin-Karp 算法使用了基于散列的子串搜索算法。

3.2 多模式匹配问题

  • 基于前缀搜索方法:搜索从前向后(沿着文本的正向)进行,逐个读入文本字符,使用在 P P P 上构建的自动机进行识别。对于每个文本位置,计算既是已读入文本的后缀,同时也是 P P P 中某个模式串的前缀的最长字符串。
    • 著名的 Aho-Corasick Automaton (AC 自动机) 算法、Multiple Shift-And 算法使用的这种方法。
  • 基于后缀搜索方法:搜索从后向前(沿着文本的反向)进行,搜索模式串的后缀。根据后缀的下一次出现位置来移动当前文本位置。这种方法可以避免读入所有的文本字符。
    • Commentz-Walter 算法(Boyer-Moore 算法的扩展算法)、Set Horspool 算法(Commentz-Walter 算法的简化算法)、Wu-Manber 算法都使用了这种方法。
  • 基于子串搜索方法:搜索从后向前(沿着文本的反向)进行,在模式串的长度为 m i n ( l e n ( p i ) ) min(len(p^i)) min(len(pi)) 的前缀中搜索子串,以此决定当前文本位置的移动。这种方法也可以避免读入所有的文本字符。
    • Multiple BNDM 算法、Set Backward Dawg Matching (SBDM) 算法、Set Backwrad Oracle Matching (SBOM) 算法都使用了这种方法。

需要注意的是,以上所介绍的多模式串匹配算法大多使用了一种基本的数据结构:「字典树(Trie Tree)」。著名的 「Aho-Corasick Automaton (AC 自动机) 算法」 就是在 KMP 算法的基础上,与「字典树」结构相结合而诞生的。而「AC 自动机算法」也是多模式串匹配算法中最有效的算法之一。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值