1、字符串移位包含問題
//普通解法 bool contain_check() { char s[6] = "AABCD"; char d[5] = "CDAA"; int len = strlen(s); for(int i=0; i<len; ++i) { char temp = s[0]; for(int j=0; j<len-1; ++j) s[j] = s[j+1]; s[len-1] = temp; if(strstr(s,d) != 0) return true; } return false; }
尋找規律:對S1做循環移位所得到的字符串都是字符串S1S1的子字符串,如果S2可以由S1循環移位得到,那么S2一定在S1S1上。
字符串循環同構問題:如果字符串s1可以經過有限次循環得到s2,則稱s1和s2是循環同構的。S=s1+s1為主串,s2為模式串。如果s1和s2是循環同構的,那么s2就一定可以在S中找到匹配!
2、求一個字符串中出現頻率最高的那個字符及其出現次數
空間換時間:使用一個額外的數組統計每個字符出現的次數,再掃描一次得到查找的字符,這是O(N)的時間復雜度。得到字符串第一個無重復的字符也可以用這種方法。
假設處理的是ASCII字符集:需要注意 char的范圍是[-128,127]之間,所以數組下標要加上128
void get_most(char *s, char &ch, int &size) { ch = '\0'; size = 0; if (NULL != s) { int n[256]; memset(n, 0, sizeof(n)); while(*s != '\0') { n[*s+128] += 1; if((n[*s+128]) > size) { size = n[*s+128]; ch = *s; } s++; } } }
3、給一個字符串,有大小寫字母,要求寫一個函數把小寫字母放在前面,大寫字母放在后面,盡量使用最小的空間、時間復雜度。
void move_char(char* a) { char* i = a; char* j = a+strlen(a)-1; while(i < j) { while(*i && (*i>='a' && *i<='z')) ++i; if((*i) == '\0') break; while(*j>='A' && *j<='Z') --j; if(i < j) { char c = *i; *i = *j; *j = c; } ++i; --j; } }
4、編寫字符串處理函數,將字符串中的字符'*'移到串的前部分,前面的非'*'字符后移,但不能改變非'*'字符的先后順序,函數返回串中字符'*'的數量。如原始串為:ab**cd**e*12,處理后為*****abcde12,函數並返回值為5。(要求使用盡量少的時間和輔助空間)
int partitionStar(char a[],int len) { int count = 0; int i = len-1; int j = len-1; //j指向第一個'*' while(i >= 0) { if (a[i] != '*') { swap(a[i--], a[j--]); } else { i--; count++; } } return count; }
5、刪除字符串中的數字並壓縮字符串。如字符串"abc123de4fg56"處理后變為"abcdefg"。注意空間和效率。
(下面的算法只需要一次遍歷,不需要開辟新空間,時間復雜度為O(N))
char* delete_digits(char* str) { char* i = str; // i for cursor, j for the first digit char; char* j = str; while(*i != '\0') { if (*i<'0' || *i>'9') { *j++ = *i++; } else { ++i; } } *j ='\0'; return str; }
6、刪除特定字符:寫一個高效的函數,刪除字符串里給定的字符
void Remove_chars(char str[], char remove[]) { int remove_arr[256]; for(int i=0; i<256; ++i) remove_arr[i] = 0; for(int i=0; remove[i]; ++i) remove_arr[remove[i]] = 1; int i = 0; for(int j=0; str[j]; ++j) { if(!remove_arr[str[j]]) str[i++] = str[j]; } str[i]='\0'; }
7、編碼實現字符串轉整型的函數(實現函數atoi的功能)。如將字符串“123”轉化為123,“-0123”轉化為-123
int str_to_int(const char* str) { int is_neg = 0, num = 0; const char * p = str; if (*str == '-') { is_neg = 1; ++ p; } while(*p >= '0' && *p <= '9') { num = num * 10 + (*p-'0'); ++ p; } if(is_neg) num *= -1; return num; }
編碼實現整型轉字符串的函數
//整數最大的位數 #define MAX_DIGITS_NUM 10 void int_to_str(int num,char str[]) { int i = 0, j = 0, is_neg = 0; char temp[MAX_DIGITS_NUM+2]; if(num < 0) { num *= -1; is_neg = -1; } //使用do while循環可以處理num為0的情況 do { temp[i++] = (num%10)+'0'; num /= 10; }while(num); if(is_neg) temp[i++] = '-'; while(i > 0) str[j++] = temp[--i]; str[j] = '\0'; }
8、翻轉句子中單詞的順序
題目:輸入一個英文句子,翻轉句子中單詞的順序,但單詞內字符的順序不變。句子中單詞以空格符隔開。為簡單起見,標點符號和普通字母一樣處理。例如輸入“I am a student.”,則輸出“student. a am I”。 分析:先顛倒句子中的所有字符,再顛倒每個單詞內的字符。由於單詞內的字符被翻轉兩次,因此順序仍然和輸入時的順序保持一致。
void Reverse(char *pBegin, char *pEnd) { if(pBegin == NULL || pEnd == NULL) return; while(pBegin < pEnd) { char temp = *pBegin; *pBegin = *pEnd; *pEnd = temp; pBegin ++, pEnd --; } } char* ReverseSentence(char *pData) { if(pData == NULL) return NULL; char *pBegin = pData; char *pEnd = pData; while(*pEnd != '\0') pEnd ++; pEnd--; // Reverse the whole sentence Reverse(pBegin, pEnd); // Reverse every word in the sentence pBegin = pEnd = pData; while(*pBegin != '\0') { if(*pBegin == ' ') { pBegin ++; pEnd ++; continue; } // A word is between with pBegin and pEnd, reverse it else if(*pEnd == ' ' || *pEnd == '\0') { Reverse(pBegin, --pEnd); pBegin = ++pEnd; } else { pEnd ++; } } return pData; }
9、求字符串的最長重復子串:構造字符串的后綴數組,對后綴數組排序,再兩兩比較得到最長的重復子串
//compare funciton used by qsort() int pstrcmp(const void *p, const void *q) { return strcmp(*(char **)p, *(char **)q); } //get max common length of string p and q int comlen(char *p, char *q) { int i = 0; while (*p && (*p++ == *q++)) i++; return i; } //get max repeat substring of str int find_max_repeat(char* str, char* result, int & len) { int temlen, maxi, maxlen = -1; char *a[99999]; int n = 0; while (*str != '\0') { a[n++] = str++; } qsort(a, n, sizeof(char *), pstrcmp); for (int i = 0; i < n-1; i++) { temlen = comlen(a[i], a[i+1]); if (temlen > maxlen) { maxlen = temlen; maxi = i; } } result = a[maxi]; len = maxlen; printf("%.*s\n", maxlen, result); return maxlen; }
10、字符串字母包含問題:有一個各種字母組成的字符串,還有一個字母數目較少的字符串,什么方法能最快的查出所有小字符串里的字母在大字符串里都有?
最簡單的方法:輪詢小字符串里的每個字母,看它是否同在第一個字符串里。這需要O(n*m)次操作,其中n是string1的長度,m是string2的長度。
一個改進的方法:對這兩個字符串的字母進行排序,然后同時對兩個字串依次輪詢。兩個字串的排序需要(常規情況)O(mlogm)+ O(nlogn)次操作,之后的線性掃描需要O(m+n)次操作。(隨着字串長度的增長,你會發現這個算法的效果會越來越好)
一個很好的方法:只需要O(n+m)次操作。對第一個長字串進行輪詢,把其中的每個字母都放入一個Hashtable里(成本是O(n)次操作)。然后輪詢第二個字串,在Hashtable里查詢每個字母,看能否找到,如果找不到,說明沒有匹配成功,這將消耗掉O(m)次操作。
一個更有趣的方法:對一個一定個數的字母組成的字串,給每個字母分配一個素數,從2開始,往后類推。這樣A將會是2,B將會是3,C將會是5,等等。現在先遍歷第一個字串,把每個字母代表的素數相乘,會得到一個很大的整數。然后——輪詢第二個字符串,用每個字母代表的素數除它。如果除的結果有余數,這說明有不匹配的字母,如果整個過程中沒有余數,你應該知道它是第一個字串恰好的子集了。
11、求一個字符串的最長的沒有重復字符的子串。
方法一:窮舉法,使用2重外循環遍歷所有的區間,用2重內循環檢驗子串是否符合“無重復字符”這一要求。其中外層循環i、j 遍歷所有的下標,m、n是內層循環,檢查區間[i,j]是否符合要求。空間復雜度是O(1),時間復雜度O(N^4)。
方法二:對方法一的檢驗子串是否“無重復字符”進行改進,使用hash表記錄字符是否出現過。
方法三:使用DP,對於最長不重復子串,某個當前的字符,如果它與前面的最長不重復子串中的字符沒有重復,那么就可以以它為結尾構成新的最長子串;如果有重復,那么就與某個稍短的子串構成新的子串或者單獨成一個新子串。
方法四:對這個字符串構造后綴數組,在每個后綴數組中,尋找沒有重復字符的最長前綴,就是要找的子串。
詳解:http://www.cnblogs.com/luxiaoxun/archive/2012/10/02/2710471.html
12、求一個字符串中連續出現次數最多的子串
int count = 0; char sub_str[256]; void find_str(char *str) { int str_len = strlen(str); int i, j, k; int tmp_cnt = 0; for(i = 0; i < str_len; i++) { for(j = i+1; j < str_len; j++) { int n = j-i; //sub string length tmp_cnt = 1; if(strncmp(&str[i], &str[j], n) == 0) //compare n-lengths strings { tmp_cnt++; //they are equal, so add count for(k = j+n; k < str_len; k += n) //consecutive checking { if(strncmp(&str[i], &str[k], n) == 0) { tmp_cnt++; } else break; } if(count < tmp_cnt) { count = tmp_cnt; memcpy(sub_str, &str[i], n); //record the sub string } } } } }
13、尋找包含給定字符集合的最短子串:字符串S="abcdefg",字符集合D={'c','f'},那這個最小子串為S'="cdef"。
//判斷hash是否包含所有的hash_sub int has_sub(int * hash, int * hash_sub) { for(int i=0; i<256; ++i) { if(hash_sub[i] && !hash[i]) return 0; } return 1; } //在str中尋找包含dst的最短子串 int min_substring(char * str, char * dst) { char * begin = str; char * end = str; char * begin_index = NULL; int minlen = strlen(str); int hash[256]; int hash_sub[256]; memset(hash,0,sizeof(hash)); memset(hash_sub,0,sizeof(hash_sub)); for(int i=0; dst[i]; ++i) hash_sub[dst[i]] = 1; hash[*begin] = 1; while(*end) { while(!has_sub(hash,hash_sub) && *(end+1)) { ++ end; hash[*end] = 1; } while(has_sub(hash,hash_sub)) { if(end-begin+1 < minlen) { minlen = end-begin+1; begin_index = begin; } if (*begin != *(begin+1)) { hash[*begin] = 0; } //hash[*begin] = 0; ++ begin; } if(*(end+1) == '\0') break; } printf("%.*s\n", minlen, begin_index); return minlen; }
14、遞歸求解一個字符串中連續單個字符出現最多次數字符的個數
int max_count; void count(const char *s) { if(!(*s)) return; const char * p = s+1; int n = 1; while(*p && *p == *s) { ++ n; ++ p; } if(n > max_count) max_count = n; count(s+1); }