基友前两天参加了阿里的实习生面试,问了个问题,就是关于字符串的子串搜索的问题。想想实现方式无非就是两层循环,但是 java 中是有现成实现的,于是我就去查查源码,看看 java 语言怎么实现这个的,发现也就是差不多的意思。

java.lang 包中 String 类 有几个 indexOf() 函数,我要寻找的是 indexOf(String str) 这个的具体实现,发现了

public int indexOf(String str) {
return indexOf(str, 0);
}

然后 F3 继续找,

public int indexOf(String str, int fromIndex) {
return indexOf(value, offset, count, str.value, str.offset, str.count,
fromIndex);
}

这个调用应该就是算法的实现了,继续 F3

/**
* Code shared by String and StringBuffer to do searches. The source is the
* character array being searched, and the target is the string being
* searched for.
*
* @param source
* the characters being searched.
* @param sourceOffset
* offset of the source string.
* @param sourceCount
* count of the source string.
* @param target
* the characters being searched for.
* @param targetOffset
* offset of the target string.
* @param targetCount
* count of the target string.
* @param fromIndex
* the index to begin searching from.
*/
static int indexOf(char[] source, int sourceOffset, int sourceCount,
char[] target, int targetOffset, int targetCount, int fromIndex) {
if (fromIndex >= sourceCount) {
return (targetCount == 0 ? sourceCount : -1);
}
if (fromIndex < 0) {
fromIndex = 0;
}
if (targetCount == 0) {
return fromIndex;
} char first = target[targetOffset];
int max = sourceOffset + (sourceCount - targetCount); for (int i = sourceOffset + fromIndex; i <= max; i++) {
/* Look for first character. */
if (source[i] != first) {
while (++i <= max && source[i] != first)
;
} /* Found first character, now look at the rest of v2 */
if (i <= max) {
int j = i + 1;
int end = j + targetCount - 1;
for (int k = targetOffset + 1; j < end
&& source[j] == target[k]; j++, k++)
; if (j == end) {
/* Found whole string. */
return i - sourceOffset;
}
}
}
return -1;
}

注意这个函数是静态函数,是String and StringBuffer公用的一个工具方法,具体算法原理代码中很显而易见。

又查阅了一些资料,目前子串搜索的方法有下面几种,

KMP算法, BM算法,Sunday算法

其中无论是简单程度还是效率排序均为下面:

Sunday > BM > KMP

Sunday 算法的核心思想如下(转自百度百科):

字符串模式匹配中超越BF、KMP和BM的算法
sunday算法的概念如下:
Sunday算法是Daniel M.Sunday于1990年提出的一种比BM算法搜索速度更快的算法。其核心思想是:在匹配过程中,模式串并不被要求一定要按从左向右进行比较还是从右向左进行比较,它在发现不匹配时,算法能跳过尽可能多的字符以进行下一步的匹配,从而提高了匹配效率。
假设在发生不匹配时S[i]≠T[j],1≤i≤N,1≤j≤M。此时已经匹配的部分为u,并假设字符串u的长度为L。如图1。明显的,S[L+i+1]肯定要参加下一轮的匹配,并且T[M]至少要移动到这个位置(即模式串T至少向右移动一个字符的位置)。

图1  Sunday算法不匹配的情况

分如下两种情况:
(1) S[L+i+1]在模式串T中没有出现。这个时候模式串T[0]移动到S[T+i+1]之后的字符的位置。如图2。

图2  Sunday算法移动的第1种情况

(2)S[L+i+1]在模式串中出现。这里S[L+i+1]从模式串T的右侧,即按T[M-1]、T[M-2]、…T[0]的次序查找。如果发现S[L+i+1]和T中的某个字符相同,则记下这个位置,记为k,1≤k≤M,且T[k]=S[L+i+1]。此时,应该把模式串T向右移动M-k个字符的位置,即移动到T[k]和S[L+i+1]对齐的位置。如图3。

图3  Sunday算法移动的第2种情况

依次类推,如果完全匹配了,则匹配成功;否则,再进行下一轮的移动,直到主串S的最右端结束。该算法最坏情况下的时间复杂度为O(N*M)。对于短模式串的匹配问题,该算法执行速度较快。
Sunday算法思想跟BM算法很相似,在匹配失败时关注的是文本串中参加匹配的最末位字符的下一位字符。如果该字符没有在匹配串中出现则直接跳过,即移动步长= 匹配串长度+1;否则,同BM算法一样其移动步长=匹配串中最右端的该字符到末尾的距离+1。
现举个例子来说明:
比如:
匹配串:abcbczdxzc
模式串:zbcac
这里我们看到b-a没有对上,我们就看匹配串中的z在模式串的位置,然后对齐。
匹配串:abcbczdxzc
模式串:         zbcac
如果模式串中的没有那个字符的话就跳过去。
匹配串:abcbcedxzcs
模式串:zbcac
e不在模式串中出现,那么我们就
匹配串:abcbcedxzcs
模式串:           zbcac

附一个Sunday算法的 C++ 实现(原文链接:http://hi.baidu.com/azuryy/item/8a50f54a2f8c72e51381dad3)

/* Sunday.h */
class Sunday
{
public:
Sunday();
~Sunday(); public:
int find(const char* pattern, const char* text); private:
void preCompute(const char* pattern); private:
//Let's assume all characters are all ASCII
static const int ASSIZE = 128;
int _td[ASSIZE] ;
int _patLength;
int _textLength;
}; 源文件
/* Sunday.cpp */ Sunday::Sunday()
{
} Sunday::~Sunday()
{
} void Sunday::preCompute(const char* pattern)
{
for(int i = 0; i < ASSIZE; i++ )
_td[i] = _patLength + 1; const char* p;
for ( p = pattern; *p; p++)
_td[*p] = _patLength - (p - pattern);
} int Sunday::find(const char* pattern, const char* text)
{
_patLength = strlen( pattern );
_textLength = strlen( text ); if ( _patLength <= 0 || _textLength <= 0)
return -1; preCompute( pattern ); const char *t, *p, *tx = text; while (tx + _patLength <= text + _textLength)
{
for (p = pattern, t = tx; *p; ++p, ++t)
{
if (*p != *t)
break;
}
if (*p == 0)
return tx-text;
tx += _td[tx[_patLength]];
}
return -1;
} 简单测试下:
int main() {
char* text = "blog.csdn,blog.net";
char* pattern = "csdn,blog" ;
Sunday sunday; printf("The First Occurence at: %d\n",sunday.find(pattern,text)); return 1;
}

注,上述算法中数组_td[],是用于记录 pattern 中每个字符的位置

Java 中字符串的子串搜索的更多相关文章

  1. 【转】Java中字符串中子串的查找共有四种方法(indexof())

    原文网址:http://wfly2004.blog.163.com/blog/static/1176427201032692927349/ Java中字符串中子串的查找共有四种方法,如下:1.int ...

  2. Java中字符串中子串的查找共有四种方法(indexof())

    Java中字符串中子串的查找共有四种方法(indexof()) Java中字符串中子串的查找共有四种方法,如下:1.int indexOf(String str) :返回第一次出现的指定子字符串在此字 ...

  3. Java中字符串indexof() 的使用方法

    Java中字符串中子串的查找共有四种方法(indexof())indexOf 方法返回一个整数值,指出 String 对象内子字符串的开始位置.如果没有找到子字符串,则返回-1.如果 startind ...

  4. 三张图彻底了解Java中字符串的不变性

    转载: 三张图彻底了解Java中字符串的不变性 定义一个字符串 String s = "abcd"; s中保存了string对象的引用.下面的箭头可以理解为"存储他的引用 ...

  5. java中字符串的非空判断

    问题如下:在java 中 字符串为null 如何判断String str;if(str==null) ??str.equal("null") ?? 答:我觉得应该搞清楚字符串对象和 ...

  6. java中字符串String 转 int(转)

    java中字符串String 转 int String -> int s="12345"; int i; 第一种方法:i=Integer.parseInt(s); 第二种方法 ...

  7. JAVA中字符串函数subString的用法小结

    本篇文章主要是对JAVA中字符串函数subString的用法进行了详细的介绍,需要的朋友可以过来参考下,希望对大家有所帮助 String str; str=str.substring(int begi ...

  8. Java中字符串的一些常见方法

    1.Java中字符串的一些常见方法 /** * */ package com.you.model; /** * @author Administrator * @date 2014-02-24 */ ...

  9. Java中字符串string的数据类型

    Java中字符串string的数据类型 时间:2017-07-03 08:01:47 YuanMxy 原文:https://blog.csdn.net/YuanMxy/article/details/ ...

随机推荐

  1. AOP programming paradiag

    AOP https://en.wikipedia.org/wiki/Aspect-oriented_programming Typically, an aspect is scattered or t ...

  2. Struts2之过滤器和拦截器的区别

    刚学习Struts2这个框架不久,心中依然有一个疑惑未解那就是过滤器和拦截器的区别,相信也有不少人跟我一样对于这个问题没有太多的深入了解 那么下面我们就一起来探讨探讨 过滤器,是在java web中, ...

  3. 【转】SDP file

    SDP file Introduction The Session Description Protocol (SDP) is a format for describing the initiali ...

  4. Android Monkey 测试策略【转】

    Monkey 测试针对不同的对象和不同的目的,需要采用不同的测试方案. 首先测试的对象.目的及类型如下: 测试的类型 应用程序的稳定性测试 应用程序的压力测试 测试对象 单一 apk apk 集合 测 ...

  5. css3-多列显示文字

    -------------------------- <style>            #demo{                margin: 20px auto;         ...

  6. select 通过表单提交获取select中的值

    <select class="txt" name="choice">       <option value="name" ...

  7. CDN中前端层的复制

    前端层的复制是为了提高静态内容分发的性能和可扩展性.将静态内容的分发转由边缘服务器来完成是为了解决可扩展性的问题,因为这样做可以避免出现在对等点和广域网链路处的网络拥堵风险,而这两个地方的拥堵是网络延 ...

  8. 2018年3月24日上海MVP线下技术交流活动简报

    2018年3月24日下午,几位上海MVP自发组织了一次线下的技术交流会,主要由MVP胡浩牵头,我(陈晴阳).刘鑫.朱兴亮和胡浩各自做了一次主题演讲,具体主题是: 陈晴阳:<这还是我认识的Visu ...

  9. UUID类型如何比较是否相等

    直接使用equals方法 UUID x = UUID.fromString("062db347-6a72-69a1-40c0-7516e0a26459"); UUID y = UU ...

  10. 利用可排序Key-Value DB构建时间序列数据库(简论)

    为了防止无良网站的爬虫抓取文章,特此标识,转载请注明文章出处.LaplaceDemon/ShiJiaqi. http://www.cnblogs.com/shijiaqi1066/p/5855064. ...