Leetcode: Implement strStr()
14323 ワード
Implement strStr().
Returns a pointer to the first occurrence of needle in haystack, or null if needle is not part of haystack.
難易度:60.これはアルゴリズムで古典的な問題であり,1つの文字列が別の文字列のサブ列であるかどうかを判断する.このテーマの最も古典的なアルゴリズムはKMPアルゴリズムであるべきで、よく知らない友达はKnuth–Morris–Pratt algorithmを参照することができます.KMPアルゴリズムは最良の線形アルゴリズムであり,複雑度はこの問題の下限に達している.しかし、KMPアルゴリズムは複雑で、面接の短時間で完全に正確に実現することは難しい.だから一般的に面接ではKMPアルゴリズムの実現は要求されません.
次にbrute forceのアルゴリズムについて述べ,元の列の長さがnであり,マッチング列の長さがmであると仮定する.構想は簡単で,原列の各長さmの文字列に対してマッチング列と一致するか否かを判断する.全部でn-m+1のサブ列があるので、アルゴリズムの時間複雑度はO(n-m+1)であり、temp.equals(needle)の操作複雑度をO(m)とすると(実際にはO(1)と見なすことができる)、総時間複雑度はO((n-m+1)*m)=O(n*m)、空間複雑度はO(m)である.コードは次のとおりです.
1 public class Solution {
2 public String strStr(String haystack, String needle) {
3 if (haystack == null || needle == null) return null;
4 if (needle.length() == 0) return haystack;
5 if (haystack.length() < needle.length()) return null;
6 boolean flag = false;
7 int i = 0;
8 for (; i<=haystack.length()-needle.length(); i++) {
9 String temp = haystack.substring(i, i+needle.length());
10 if (temp.equals(needle)) {
11 flag = true;
12 break;
13 }
14 }
15 if (flag == true) return haystack.substring(i);
16 else return null;
17 }
18 }
1 public class Solution {
2 public int strStr(String haystack, String needle) {
3 if (haystack==null || needle==null) return -1;
4 for (int i=0; i<=haystack.length()-needle.length(); i++) {
5 String temp = haystack.substring(i, i+needle.length());
6 if (temp.equals(needle)) {
7 return i;
8 }
9 }
10 return -1;
11 }
12 }
しかし、これらのbrute forceメソッドはO(m)のspaceであり、O(1)のspaceを使用するには、次のようにします.
1 public class Solution {
2 public int strStr(String haystack, String needle) {
3 if(haystack==null || needle == null || needle.length()>haystack.length())
4 return -1;
5 for(int i=0;i<=haystack.length()-needle.length();i++)
6 {
7 boolean successFlag = true;
8 for(int j=0;j<needle.length();j++)
9 {
10 if(haystack.charAt(i+j)!=needle.charAt(j))
11 {
12 successFlag = false;
13 break;
14 }
15 }
16 if(successFlag)
17 return i;
18 }
19 return -1;
20 }
21 }
ネット上でrolling hashという線形アルゴリズムが見られ、具体的に知りたい人はRolling hash - Wikipediaを参照することができます.基本思想は1つのhashcodeで1つの文字列を表すことであり,hashの一意性を保証するために,文字セットより大きい素数をベースとし,この素数のべき乗をベースとする.例えば、文字セットは小文字セットであり、素数29をベースとする.例えば文字列「abacd」は、hashcode=1+2*29+1*29^2+3*29^3+4*29^4に変換されます.次に、マッチング列が元の列である「abacde」、マッチング列の長さが5であるなど、前のステップで新しいhashcodeを計算する方法です.以上の方法で「abacd」のhashcode=hを計算すると、次のステップ「bacde」「のhashcode=h/29+5*29^4.これはconstantの動作であるため、すべてのサブストリングの時間的複雑さを検出するにはO(m+n-m)=O(n)しか必要なく、線形アルゴリズムでもある.コードは以下の通りである.
1 public String strStr(String haystack, String needle) {
2 if(haystack==null || needle==null) return null;
3 if(haystack.length()==0){
4 return needle.length()==0?"":null;
5 }
6 if(needle.length()==0) return haystack;
7 if(haystack.length()<needle.length()) return null;
8
9 int base = 29;
10 long patternHash = 0;
11 long tempBase = 1;
12
13 for(int i=needle.length()-1; i>=0; i--){
14 patternHash += (int)needle.charAt(i)*tempBase;
15 tempBase *= base;
16 }
17
18 long hayHash = 0;
19 tempBase = 1;
20 for(int i=needle.length()-1; i>=0; i--){
21 hayHash += (int)haystack.charAt(i)*tempBase;
22 tempBase *= base;
23 }
24 tempBase /= base;
25
26 if(hayHash == patternHash){
27 return haystack;
28 }
29
30 for(int i=needle.length(); i<haystack.length(); i++){
31 hayHash = (hayHash - (int)haystack.charAt(i-needle.length())*tempBase)*base+(int)haystack.charAt(i);
32 if(hayHash == patternHash){
33 return haystack.substring(i-needle.length()+1);
34 }
35 }
36 return null;
37 }