问题描述:
对于一个给定的 source 字符串和一个 target 字符串,你应该在 source 字符串中找出 target 字符串出现的第一个位置(从0开始)。如果不存在,则返回 -1。
样例 1:
输入: source = “source” , target = “target”
输出:-1
样例解释: 如果source里没有包含target的内容,返回-1
样例 2:
输入: source = “abcdabcdefg” ,target = “bcd”
输出: 1
样例解释: 如果source里包含target的内容,返回target在source里第一次出现的位置
解答思路:
一:第一想法:
想必大家想到的第一个方法就是:循环遍历source里面的每个值,依次和target里面的每个值比较,比如从source的第0为开始,比对0到targetLength长度的值是否和target的第0位到targetLength的值一一对应,不对应则比对source的第1位往后targetLength长度值和target比较,一直循环到结束
能理解继续往下:
比如说下图:我比对source的第1位到第6位是否和target的第0位到第5位相等,当我比对到第5位的时候,发现和target的目标字符不匹配了,我需要将target整体往后挪一位(相对于source而言)像图2那样,再重新比较source的第2位到第7位和target的第0位到第5位是否相等,见图2
图(1)
图(2)
二:思维跳跃一下
思考:可不可以从图1直接跳到图3呢,
再把图1重新贴出来:
图(1)
图(3)
从图1直接变成图3意味着:
由原本的target在source的第一位直接挪到第4位(相对于Source而言),此时source的第4位和target的第0位相等,所以直接比较source的第5位和target的第1位,
回顾一下图1:
图1的时候比较到source的第5位和target的第4位的时候发现不匹配,
图3是从source的第5位和target的第1位开始比较,
从图1直接跳转到图3意味着 : source的索引不用变,target的索引更改
可以看出从图1直接到图3可是省略了好几部呢
三:思维跳跃多几下
对于这一部分如果能充分理解图1跳到图2完全没问题的,可以大致瞄瞄就好
我们来思考一下,如果图1中匹配的那一段内容即黄色部分不是abdab而是abdac,像图多1-1那样
图多1-1
那还能像之前图1直接跳到图3那样,直接跳到图多1-3,然后直接比较source的第5位和target的第1位么,如下图
图多1-3
很显然不能,此时source的第4位和target的第0位都不匹配,怎么可能存在比较target的第1位和source的第5位的情况呢?
对于图1图3和图多1-1图多1-3我们可以知道当我们出现source的第4位和target的第3位匹配的情况时,能直接跳转到图3的条件只有当target的第0和source的第4相等才可以,也就是target的第0和target的第3相等
之前说过图1是比较到source的第5位和target的第4位,图3是直接从source的第5位和target的第1位开始比较。
这里我们又知道,图1到图3之所以能直接的条件是:target第4位之前2个长度字符串和target的0-第2位字符串相等。
四:总结记录思维
再再说一下,由图1能直接去图3是因为target的0开始2位长度串==第4位往前2位长度字符串
那么我们是不是可以记录一下target第0位往后x个长度串==第y位往前x个字符串,只有有什么好处呢,当我们和source比较到一定位置的时候,发现不匹配了,我们可以像图1-图3那样不改变source的索引,把target移动到合适的位置,继续比较就行了(即那个思维跳转一下)
我们有一个数组,每个数组的索引记为y,数组的值记为x,next[y]=x;比如说对于图1或图3那个target字符串产生的数组next[4]=1(2个长度,但是索引从0开始,所以是1),当target比较到第4位发现和source的特定位不匹配了,那么target的索引改为1,source索引不变,继续比较
上一段很重要哦,结合图1-图3看看
五:产生next[] 值
我们知道next[] 产生的值是target从0到x个字符==y往前x个字符(不包括当前第y位)
0.对于target的第0位来说,第0位往前无值,所以next[0]=0; (表示的意思是当target的第0位和source不匹配时,target的下次要匹配位置依然是0,该是source改变索引的时候了)
图next-1
1.对于target的第1位来说,他往前0个字符和第0位往后0个字符相等,所以next[1]=0;
2.next[2]=0;
3.
对于target的第3位来说,他往前1个字符和第0位往后1个字符相等,所以next[3]=1-1;这里需要说明一下,索引是从0开始的,(由于不相等我也记录为0,各位各自实现的实现可以区分开来,比如用-1代替不相等的)
…
最后得到next数组为[0,0,0,0,1,0];
六:赋上代码
这个和网上流程的不太相同,是按照我自己的理解实现的,是正确的不用怀疑,在lintCode上测试通过了的,?
public int[] getNext(String ps) {
char[] p = ps.toCharArray();
int[] next = new int[p.length];
if(next.length > 0) {
next[0] = 0;
}
int i = 1;
int j = 0;
while (i < p.length) {
if (p[i] == p[j]) {
next[i] = j;
i++;
j++;
} else {
j=0;
next[i]=j;
i++;
}
}
return next;
}
另外附上source不变,更改target的索引来查找target在source中位置的代码
public int strStr(String source, String target) {
// Write your code here‘
char[] t = source.toCharArray();
char[] p = target.toCharArray();
int i = 0; // 主串的位置
int j = 0; // 模式串的位置
int[] next = getNext(target);
while (i < t.length && j < p.length) {
if (t[i] == p[j]) { // 当j为-1时,要移动的是i,当然j也要归0
i++;
j++;
} else {
if(j==0) {
i++;
}
j = next[j]; // j回到指定位置
}
}
if (j == p.length) {
return i - j;
} else {
return -1;
}
}
七:完整解答
public class Solution {
public int[] getNext(String ps) {
char[] p = ps.toCharArray();
int[] next = new int[p.length];
if(next.length > 0) {
next[0] = 0;
}
int i = 1;
int j = 0;
while (i < p.length) {
if (p[i] == p[j]) {
next[i] = j;
i++;
j++;
} else {
j=0;
next[i]=j;
i++;
}
}
return next;
}
/**
* @param source:
* @param target:
* @return: return the index
*/
public int strStr(String source, String target) {
// Write your code here‘
char[] t = source.toCharArray();
char[] p = target.toCharArray();
int i = 0; // 主串的位置
int j = 0; // 模式串的位置
int[] next = getNext(target);
while (i < t.length && j < p.length) {
if (t[i] == p[j]) { // 当j为-1时,要移动的是i,当然j也要归0
i++;
j++;
} else {
if(j==0) {
i++;
}
j = next[j]; // j回到指定位置
}
}
if (j == p.length) {
return i - j;
} else {
return -1;
}
}
}