字符串筆試題


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);
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM