程式設計之美7:字串,那些你必須要會的事。
本系列收錄了常見字串面試和筆試中的八道題,更新於2015年4月23日。
如果有問題或想法,請直接留言,交流。
題目一:字串移位包含
問題描述:
給定兩個字串s1和s2,要求判定s2是否能夠被通過迴圈移位得到的字串包含。例如,給定s1 = AABCD和s2 = CDAA,返回true;給定s1 = ABCD和s2 = ACBD,返回false.
問題解答:
解法一:列舉
暴力求解是我們的第一反應,題目要求判定s2是否能被通過迴圈移位得到的字串包含,那我們就把s1和它所有的迴圈移位全部求出來,一個一個判斷是否存在字串包含。話不多說,見程式碼:
#include <iostream>
#include <string>
using namespace std;
bool isContain(char *src, char* dest);
void main()
{
char s1[] = "AABCD";
char s2[] = "CDAA";
int len = strlen(s1); //總共可移位length-1次
char temp = s1[len - 1];
if (isContain(s1, s2))
{
cout << "包含" << endl;
return ;
}
for (int i = 0; i < len - 1; i++)
{
for (int j = len - 1; j > 0; j--)
{
s1[j] = s1[j - 1];
}
s1[0] = temp;
temp = s1[len - 1];
//一次右移完畢
if (isContain(s1, s2))
{
cout << "包含" << endl;
return ;
}
}
cout << "不包含" << endl;
system("pause");
}
bool isContain(char *src, char* dest)
{
if (NULL != strstr(src, dest))
return true;
return false;
}
解法二:規律分析法
我們隊迴圈移位之後的結果進行分析,以s1 = “ABCD”而言,我們將ABCD所有的移位之後的字串序列寫出來,如下所示:
ABCD -> DABC -> CDAB -> BCDA
可以發現,s1及其所有迴圈移位的序列都是ABCDABCD的子序列。那麼如果s2是s1的迴圈移位子序列,那麼s2肯定是s1s1的子序列。那麼原題就轉換成判斷s2是不是s1s1的子序列了,不需要再進行迴圈移位的操作了。有同學肯定會疑問,屬於s1s1的子序列,並不一定是s1迴圈移位的子序列啊。但是同學們可以試一試,屬於s1s1的子序列,並且長度等於或者小於s1的子序列一定是s1的迴圈移位子序列。那麼程式碼就可以變成:
#include <iostream>
#include <string>
using namespace std;
bool isContain(char *src, char* dest, int lenSrc, int lenDest);
void main()
{
char s1[] = "AABCD";
char s2[] = "CDAAF";
int len1 = strlen(s1); //總共可移位length-1次
int len2 = strlen(s2);
if (isContain(s1, s2, len1, len2))
cout << "包含" << endl;
else
cout << "不包含" << endl;
system("pause");
}
bool isContain(char *src, char* dest, int lenSrc, int lenDest)
{
if (lenDest > lenSrc)
return false;
char *doubleSrc = new char[2 * lenSrc + 1]();
strcpy_s(doubleSrc, 2 * lenSrc + 1, src);
strcat_s(doubleSrc, 2 * lenSrc + 1, src);
doubleSrc[2 * lenSrc + 1] = '\0';
if (NULL == strstr(doubleSrc, dest))
return false;
return true;
}
題目二:計算字串相似度
問題描述:
為計算將str1變換到str2所需最小操作步驟,必須先對變換操作進行定義:
1.修改一個字元(如把“a”替換為“g”);
2.增加一個字元(如把“abcd”變為“abcdz”);
3.刪除一個字元(如把“travelling”變為“traveling”);
字串變換過程中執行了上述三個操作之間的任一操作,則兩字串間距離就加1。例如將上文中的str1變換到str2,即“abcd”到“gbcdz”,通過“abcd”->(操作1)->“gbcd”->(操作2)->“gbcdz”,需進行一次修改和一次新增字元操作,故兩字串距離為2,那麼字串相似度則為距離+1的倒數,即1/3=0.333。這是由俄羅斯科學家Vladimir Levenshtein在1965年提出這個概念。因此也叫Levenshtein Distance。
問題解答:
解法一:遞迴法
很多時候看起來不知道怎麼下手的題目,可以嘗試一下遞迴法。遞迴法的思想就是把大問題不斷轉換成小問題。我們看下面一個例子,A=xabcdae和B=xfdfa,先比較第一個元素,相等,那麼直接進入到下一個字元的比較。如果不相等,我們可以進行如下的操作,使得這個不相等的字串相等起來。
操作1:刪除A串的第一個字元,然後計算A[1,…, LenA - 1]和B[0, …., LenB - 1]的距離;
操作2:刪除B串的第一個字元,然後計算A[0,…, LenA - 1]和B[1, …., LenB - 1]的距離;
操作3:將A的第一個字元修改為B的第一個字元,然後計算A[1,…, LenA - 1]和B[1, …., LenB - 1]的距離;
操作4:將B的第一個字元修改為A的第一個字元,然後計算A[1,…, LenA - 1]和B[1, …., LenB - 1]的距離;
操作5:增加B的第一個字串到A的第一個字串前面,然後計算A[0,…, LenA - 1]和B[1, …., LenB - 1]的距離;
操作6:增加A的第一個字串到B的第一個字串前面,然後計算A[1,…, LenA - 1]和B[0, …., LenB - 1]的距離;
我們並不在乎具體需要什麼操作(刪除,修改或新增),我們只關心最後的距離,所以我們將上面六種操作總結一下就是
操作1:一步操作之後,然後計算A[1,…, LenA - 1]和B[0, …., LenB - 1]的距離。比如這個操作可以是刪除A串的第一個字元或者是將B的第一個字元修改為A的第一個字元,但是我們不關心具體的操作;
操作2:一步操作之後,然後計算A[0,…, LenA - 1]和B[1, …., LenB - 1]的距離;
操作3:一步操作之後,然後計算A[1,…, LenA - 1]和B[1, …., LenB - 1]的距離;
遞迴結束的條件:
上面的操作1-操作3只是告訴我們寫程式的時候有這麼三個分支,遞迴總是要有退出或者返回條件的啊。所以下面我們討論一下,這個遞迴程式幾個技術時的情況。
由上面的分析,我們可以大概知道,肯定要傳入的引數是,A字串和B字串的地址,然後A/B的起始和末尾索引,假設分別為aStartIndex/bStartIndex和aEndIndex/bEndIndex。結束的三種情況:
情況一:A字串全部遍歷完了,那麼step保留的是前面操作的步驟,bEndIndex - bStartIndex + 1保留的時候的字串的距離,所以返回bEndIndex - bStartIndex + step + 1
情況二:B字串全部遍歷完了,那麼step保留的是前面操作的步驟,bEndIndex - bStartIndex + 1保留的時候的字串的距離,所以返回bEndIndex - bStartIndex + step + 1
下面直接看程式碼吧:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int min2(int a, int b);
int min3(int a, int b, int c);
int calc(char* a, char* b, int aStartIndex, int aEndIndex, int bStartIndex, int bEndIndex, int step);
int main(void)
{
char *a = "abdd";
char *b = "aebdd";
int step = 0;
printf("%d",calc(a, b, 0, strlen(a) - 1, 0, strlen(b) - 1, step));
return EXIT_SUCCESS;
}
int calc(char* a, char* b, int aStartIndex, int aEndIndex, int bStartIndex, int bEndIndex, int step)
{
int d1,d2,d3;
if (aStartIndex > aEndIndex)
{
return bEndIndex - bStartIndex + step + 1;
}
if (bStartIndex > bEndIndex)
{
return aEndIndex - aStartIndex + step + 1;
}
if (a[aStartIndex] == b[bStartIndex])
{
//如果該位置處兩個字串的字元相等,那麼perfect,直接進入下一個位置的比較
return calc(a, b, aStartIndex + 1, aEndIndex, bStartIndex + 1, bEndIndex, step);
}
else
{
step ++;
d1 = calc(a, b, aStartIndex + 1, aEndIndex, bStartIndex, bEndIndex, step);
d2 = calc(a, b, aStartIndex, aEndIndex, bStartIndex + 1, bEndIndex, step);
d3 = calc(a, b, aStartIndex + 1, aEndIndex, bStartIndex + 1, bEndIndex, step);
return min3(d1, d2, d3);
}
}
int min2(int a, int b)
{
return a>b ? b : a;
}
int min3(int a, int b, int c)
{
if(a > b)
a = b;
if(a > c)
a = c;
return a;
}
很顯然,遞迴方法最為詬病的就是重複計算的問題,用遞迴方法的很多都有這個問題。那我們這時候呢,又可以拿出我們的第二把刷子啦,叫做”動態遞迴法“,動態遞迴的主要原理就是儲存小規模時候程式執行的結果,然後計算大規模資料的時候就可以直接呼叫小規模的結果了。
解法二:動態遞迴法
設str1=“abcd”,str2=“gbcdz”,定義一個二維陣列d[][],d[i][j]表示str1中取前i個字元和str2中取前j個字元的最短距離,例如d[3][2]表示“abc”到“gb”的最短距離。
d[i][j]的計算規則有三條:
- 來自d[i - i][j - 1],即 “str1的前i-1個字元組成的子串” 到 “str2的前j-1個字元組成的子串” 的最小距離,此時如果str1[i] = str2[j],則最短距離不變,否則最短距離加1(即把str1[i]變為str2[j] ),所以d[i][j] = d[i - 1][j - 1] + (str1[i] == str2[j] ? 0 : 1)
- 來自d[i - 1][j],即 “A的前i-1個字元組成的子串” 到 “B的前j個字元組成的子串” 的編輯距離。此時刪除在A的第i個位置上的字元即可,所以d[i][j] = d[i - 1][j] + 1
- 來自d[i][j - 1], 即 “A的前i個字元組成的子串” 到 “B的前j-1個字元組成的子串” 的編輯距離。此時在A的子串後面新增一個字元B[j]即可,所以d[i][j] = d[i][j - 1] + 1
於是狀態轉移方程:d[i][j] = min (d[i - 1][j - 1] + (str1[i] == str2[j] ? 0 : 1) , d[i - 1][j] + 1 , d[i][j - 1] + 1)
例如str1=“abcd”,str2=“gbcdz”的d[][]就為(注意i,j的取值範圍):
程式碼:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int min(int a, int b, int c);
int calc(char* a, char* b);
int main(void)
{
printf("%d",calc("abcd", "gbcdz"));
return EXIT_SUCCESS;
}
int min(int a, int b, int c)
{
if(a > b)
a = b;
if(a > c)
a = c;
return a;
}
int calc(char* a, char* b)
{
int lenA = strlen(a);
int lenB = strlen(b);
int **d = new int*[lenA + 1];
for (int i = 0; i < lenA + 1; i++)
{
d[i] = new int[lenB + 1];
}
for(int i = 0;i <= lenA; ++i)
d[i][0] = i;
for(int i = 0; i <= lenB; ++i)
d[0][i] = i;
for(int i = 1; i <= lenA; ++i)
for(int j = 1; j <= lenB; ++j)
d[i][j] = min(d[i-1][j-1] + (a[i-1] == b[j-1] ? 0:1), d[i-1][j]+1, d[i][j-1]+1);
return d[lenA][lenB];
}
題目三:如何輸出字串所有組合和全排列
第一小題:如何輸出字串的所有組合?例如:”abc“輸出a、b、c、ab、ac、bc、abc,假設字串中所有的字元都不重複。
這道題可以位運算來巧妙的解答。假設原有元素 n 個,則最終組合結果是 2n−1 個。我們可以用位操作方法:假設元素原本有:a,b,c 三個,則 1 表示取該元素,0 表示不取。故取a則是001,取ab則是011。所以一共三位,每個位上有兩個選擇 0 和 1。而000沒有意義,所以是
這些結果的點陣圖值都是 1,2…
001,010,011,100,101,110,111 。對應輸出組合結果為:a,b,ab,c,ac,bc,abc。
因此可以迴圈 1~
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void combination(char *str);
void AllRange(char* str,int start,int length);
void Permutation(char* str);
void Swap(char *a ,char *b);
void main(void)
{
char src[] = "abc";
combination(src);
Permutation(src);
}
//求全組合,如abc的全組合為a b c ab ac bc abc
void combination(char *str)
{
if (NULL == str)
return;
int len = strlen(str);
int maxN = 1 << len;
for (int i = 1; i < maxN; i++) //外層迴圈從1 ~ 2^n - 1
{
for (int j = 0; j < len; j++)
{
//判斷len位的二進位制哪一位是1
if (i & (1 << j))
{
printf("%c", str[j]);
}
}
printf(" ");
}
}
第二小題:如何輸出字串中字元的全排列?例如:”abc“可以輸出abc、acb、bac、bca、cab和cba。
所謂全排列,就是打印出字串中所有字元的所有排列。例如輸入字串abc,則打印出 a、b、c 所能排列出來的所有字串 abc、acb、bac、bca、cab 和 cba 。
一般最先想到的方法是暴力迴圈法,即對於每一位,遍歷集合中可能的元素,如果在這一位之前出現過了該元素,跳過該元素。例如對於abc,第一位可以是 a 或 b 或 c 。當第一位為 a 時,第二位再遍歷集合,發現 a 不行,因為前面已經出現 a 了,而 b 和 c 可以。當第二位為 b 時 , 再遍歷集合,發現 a 和 b 都不行,c 可以。可以用遞迴或迴圈來實現,但是複雜度為 O(n^n) 。有沒有更優雅的解法呢。
首先考慮bac和cba這二個字串是如何得出的。顯然這二個都是abc中的 a 與後面兩字元交換得到的。然後可以將abc的第二個字元和第三個字元交換得到acb。同理可以根據bac和cba來得bca和cab。
因此可以知道 全排列就是從第一個數字起每個數分別與它後面的數字交換,也可以得出這種解法每次得到的結果都是正確結果,所以複雜度為 O(n!)。找到這個規律後,遞迴的程式碼就很容易寫出來了:
#include<stdio.h>
#include<string>
//交換兩個字元
void Swap(char *a ,char *b)
{
char temp = *a;
*a = *b;
*b = temp;
}
//遞迴全排列,start 為全排列開始的下標, length 為str陣列的長度
void AllRange(char* str,int start,int length)
{
if(start == length-1)
{
printf("%s\n",str);
}
else
{
for(int i=start;i<=length-1;i++)
{ //從下標為start的數開始,分別與它後面的數字交換
Swap(&str[start],&str[i]);
AllRange(str,start+1,length);
Swap(&str[start],&str[i]);
}
}
}
void Permutation(char* str)
{
if(str == NULL)
return;
AllRange(str,0,strlen(str));
}
void main()
{
char str[] = "abc";
Permutation(str);
}
引申:去重的全排列
如果我們輸入的是abb,那麼第一個字元與後面的交換後得到 bab、bba。然後abb中,第二個字元和第三個就不用交換了。但是對於bab,它的第二個字元和第三個是不同的,交換後得到bba,和之前的重複了。因此,這種方法不行。
因為abb能得到bab和bba,而bab又能得到bba,那我們能不能第一個bba不求呢? 我們有了這種思路,第一個字元a與第二個字元b交換得到bab,然後考慮第一個字元a與第三個字元b交換,此時由於第三個字元等於第二個字元,所以它們不再交換。再考慮bab,它的第二個與第三個字元交換可以得到bba。此時全排列生成完畢,即abb、bab、bba三個。
這樣我們也得到了在全排列中去掉重複的規則:去重的全排列就是從第一個數字起每個數分別與它後面非重複出現的數字交換。用程式設計的話描述就是第i個數與第j個數交換時,要求[i,j)中沒有與第j個數相等的數。下面給出完整程式碼:
#include<stdio.h>
#include<string>
//交換兩個字元
void Swap(char *a ,char *b)
{
char temp = *a;
*a = *b;
*b = temp;
}
//在 str 陣列中,[start,end) 中是否有與 str[end] 元素相同的
bool IsSwap(char* str,int start,int end)
{
for(;start<end;start++)
{
if(str[start] == str[end])
return false;
}
return true;
}
//遞迴去重全排列,start 為全排列開始的下標, length 為str陣列的長度
void AllRange2(char* str,int start,int length)
{
if(start == length-1)
{
printf("%s\n",str);
}
else
{
for(int i=start;i<=length-1;i++)
{
if(IsSwap(str,start,i))
{
Swap(&str[start],&str[i]);
AllRange2(str,start+1,length);
Swap(&str[start],&str[i]);
}
}
}
}
void Permutation(char* str)
{
if(str == NULL)
return;
AllRange2(str,0,strlen(str));
}
void main()
{
char str[] = "abb";
Permutation(str);
}
題目四:字串逆序
字串逆序
給定一個字串s,將s中的字元順序顛倒過來,比如s=”abcd”,逆序後變成s=”dcba”。申請一個新的一樣長度的空間,然後從字串尾端逆序複製到新的字元陣列這種方法就不說了。一般都要要求原地逆序,也就是不能申請額外的記憶體空間。那麼這個可以用首尾交換法,也很簡單。但是交換的時候會用到臨時變數,如果要求不能使用臨時變數的話,那麼可以利用異或操作來完成交換的功能。
異或操作是:相同則為0,不同則為1.如1^1 = 0,而1 ^ 0 = 1。我們假設要交換的兩個數為A和B,那麼
A = A ^ B
B = A ^ B = ( A ^ B) ^ B = A
A = B ^ A = A ^ (A ^ B) = B
請見程式碼:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *reverse1(char *src);
char *reverse2(char *src);
void main(void)
{
char src[] = "aba";
//printf("%s \n", reverse1(src));
printf("%s \n", reverse2(src));
}
//原地逆序方法一:首尾進行交換
char *reverse1(char *src)
{
int len = strlen(src);
char temp;
for (int i = 0; i < len/2; i++)
{
temp = *(src + i);
*(src + i) = *(src + len - 1 - i);
*(src + len - 1 - i) = temp;
}
return src;
}
//原地逆序方法二:不使用臨時變數來交換
char *reverse2(char *src)
{
int len = strlen(src);
char temp;
for (int i = 0; i < len/2; i++)
{
*(src + i) = (*(src + i)) ^ (*(src + len - 1 - i));
*(src + len - 1 - i) = (*(src + i)) ^ (*(src + len - 1 - i));
*(src + i) = (*(src + i)) ^ (*(src + len - 1 - i));
}
return src;
}
按照單詞逆序
給定一個字串,按單詞將該字串逆序,比如給定”This is a sentence”,則輸出是”sentence a is This”,為了簡化問題,字串中不包含標點符號。
解法:當然如果這道題會用c++的話,很簡單,把每一個單詞都存進一個vector或者map,然後逆序輸出。當然這種解法肯定不是我們需要討論的問題。我們要做的就是迴歸原始。
分兩步
1 先按單詞逆序得到”sihT si a ecnetnes”
2 再整個句子逆序得到”sentence a is This”
見程式碼:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *reverse2(char *src, int start, int end);
char *reverseWords(char *src);
void main(void)
{
char src[] = "This is a sentence";
printf("%s \n", reverseWords(src));
}
char *reverseWords(char *src)
{
int start = 0;
//先一個一個單詞來逆序
for (int i = 0; i <= strlen(src); i++)
{
if (src[i] == ' ' || i == strlen(src))
{
src = reverse2(src, start, i - 1);
start = i + 1;
}
}
//後面整個句子逆序
return reverse2(src, 0, strlen(src) - 1);
}
//原地逆序方法二:不使用臨時變數來交換
char *reverse2(char *src, int start, int end)
{
if (end == start)
return src;
for (int i = start; i <= (start + end) / 2; i++)
{
*(src + i) = (*(src + i)) ^ (*(src + start + end - i));
*(src + start + end - i) = (*(src + i)) ^ (*(src + start + end - i));
*(src + i) = (*(src + i)) ^ (*(src + start + end - i));
}
return src;
}
題目五:如何統計一行字串中有多少個單詞?
太簡單,直接看程式碼。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int getWordsCount(char *src);
void main(void)
{
char src[] = "This is a sentence";
printf("%d \n", getWordsCount(src));
}
int getWordsCount(char *src)
{
int result = 0;
int forStartIndex = 0;
//去除一行開頭的空格
for (int i = 0; i < strlen(src); i++)
{
if (src[i] != ' ')
{
forStartIndex = i;
break;
}
}
for (int i = forStartIndex; i < strlen(src); i++)
{
//如果當前位為空格,且下一位是字元
if (src[i] == ' ' && src[i + 1] != ' ')
result++;
}
//單詞數目 = 空格數目 + 1
return result + 1;
}
題目六:自己編寫字串庫函式strcpy()。
使用下面程式碼一個需要注意的問題就是,當目的字元分配的空間大於源字元分配的空間時,拷貝之後,目的字元的長度會減小,請見程式碼str拷貝到dst2。
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>
#include <assert.h>
char* strcpyOwn(char *strDest, const char* strSrc);
void main(void)
{
char *src = "Hello world";
char dst1[] = "goo";
char dst2[] = "good night, guys";
printf("%s\n", strcpyOwn(dst1, src));
printf("%s\n", strcpyOwn(dst2, src));
}
//自己實現字串拷貝函式
char *strcpyOwn(char *strDest, const char *strScr)
{
char *address=strDest;
assert((strDest != NULL) && (strScr != NULL));
while(*strScr != '\0' && *strDest != '\0')
{
*strDest++ = *strScr++;
}
*strDest = '\0';
return address;
}
題目七:如何找出一個字串中第一個只出現一次的字元
不解釋,太簡單,看程式碼
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char getFirstAppearOnceChar(char *src);
void main(void)
{
char src[] = "ThhiTs##%is a sentence";
printf("%c \n", getFirstAppearOnceChar(src));
}
//找出第一個只出現一次的字元
char getFirstAppearOnceChar(char *src)
{
//因為ASCII碼只有127個,那麼我們將申請一個長度為128的int型陣列,用來記錄每個ASCII碼出現的次數
int *times = new int[128]();
for (int i = 0; i < strlen(src); i++)
{
times[src[i] - NULL]++;
}
//再次遍歷原字串陣列,選出第一個出現一次的字元
for (int i = 0; i < strlen(src); i++)
{
if (1 == times[src[i] - NULL])
return src[i];
}
}
題目八:字元轉整數
這道題目需要注意的細節比較多,總結一下:
- 非法字元問題
- 如果存在非法字元,怎麼處理?返回什麼?
- 第一個字元如果是”+“號或者”-“,怎麼處理?
- 溢位的問題怎麼處理?
- 溢位問題上,正數和負數能表示的極限值是不一樣的,處理上會有不同嗎?
帶著這些問題,小夥伴們看程式碼吧。關鍵地方我都有寫註釋,哪裡有問題請留言哈!
程式碼:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
bool strToInt(const char* src, int *num);
bool isValidChar(char cBit);
void main(void)
{
char src[] = "-2147483648";
int result = 0;
if (false == strToInt(src, &result))
printf("轉換失敗,請檢查是否有非法字元或者是否存在溢位問題!\n");
else
printf("%d \n", result);
}
//字串轉整數
bool strToInt(const char* src, int *num)
{
int bitInt = 0; //每一位字元對應的整數
bool isSmallerThanZero = false; //是否是複數
int forStartIndex = 0;
//先處理字串的第一個字元,判斷是不是正負號
if (src[0] == '-')
{
isSmallerThanZero = true;
forStartIndex = 1;
}
else if(src[0] == '+')
{
forStartIndex = 1;
}
else if(!isValidChar(src[0]))
{
return false;
}
for (int i = forStartIndex; i < strlen(src); i++)
{
if (false == isValidChar(src[i]))
return false;
bitInt = src[i] - '0';
//因為負數溢位的範圍和正數溢位的範圍不一樣,所以要分開處理。如int型表示的整數的範圍為-2^31 -> 2^31
if (false == isSmallerThanZero)
{
if ((*num) * 10 > INT_MAX - bitInt)
return false; //再接下去會溢位,直接返回false;
(*num) = (*num) * 10 + bitInt; //不會溢位
}
else
{
if ((*num) * 10 < INT_MIN + bitInt)
return false;
*(num) = (*num) * 10 - bitInt;
}
}
return true;
}
//判斷是否是非法字元,只判斷是否是0-9
bool isValidChar(char cBit)
{
if (cBit < '0' || cBit > '9')
return false;
return true;
}