字符串--算法总结

1.字符串的复制

char *scopy(char *str1,const char *str2);  
int main()  
{  
    char s1[50];  
    scopy(s1,"I am happy!");  
    printf("%s\n",s1);  
    return 0;  
}  
  
char *scopy(char *str1,const char *str2)  
{  
    int i=0,j=0;  
    while(str2[i]!='\0')  
    {  
        str1[j]=str2[i];  
        i++;  
        j++;  
    }  
    str1[j]='\0';  
    return str1;  
}  

2.字符串的回文

判断字符串是否是回文

#include <iostream>
#include <string>
using namespace std;
int main(){
    string str;     
    while(cin>>str){
        bool flag=true;
        int i,j;        
        for(j=str.size()-1,i=0;j>=0;j--,i++){
            if(str[j]!=str[i]){
                flag=false;
                break;
            }                      
        }
        if(flag) cout<<"Yes!"<<endl;
        else cout<<"No!"<<endl;    
    }
    return 0;
}

找字符串中最长回文子串(马拉车)

      计算字符串中最长回文子串,我们使用了一下5种方法:
1)暴力求解,不用将奇数字符串和偶数字符串分开讨论,只需要前向和后向确定元素的工作同时进行,通过不断从左边缩小字符串来求出其回文子串,进而得到最长的回文子串;
2)中心拓展法:遍历每个字符,然后以该字符为中心向左右两边进行拓展,从左向右移动,就可以获取最长的回文子串的,但是需要注意奇数和偶数回文子串的不同,需要进行不同的处理;
3)中心拓展发的优化:区分技术和偶数显得程序显得非常冗余,为什么不将原来的字符串进行一个变形,使得程序只用针对奇数或者偶数类型进行处理呢?我们这儿选择将原始的字符串变形为奇数字符串进行处理;
4)动态规划:因为我们知道最长回文字符串的子串一定为回文串,因此我们可以利用动态规划法则进行求解,利用map[i][j]判断从i到j是否为回文数组,如果s[i-1]==s[j+1],就可以在O(1)的时间内判断[i-1,j+1]是否为回文了;

5)manacher算法,针对最长回文串的求解,我们使用Manacher算法可以将算法的时间复杂度控制在线性时间内。

#include <iostream>
#include <string>
#include <stack>
#include <vector>
#include <deque>
using namespace std;
/*
针对求解最长回文子串,我们分别尝试了5种方法进行求解:
1)暴力算法,时间复杂度非常高;
2)分类暴力算法,分别针对奇数和偶数进行分析;
3)分类暴力算法优化,通过构建新的字符串避免进行奇偶分析;
4)动态规划法则进行求解,通过回文淄川的子类一定是回文子串,利用动态规划算法进行求解;
5)利用最常间的Manacher算法进行求解
*/ //暴力算法
int longest_ror_String(string s,vector<char>& v){
    int len = s.length();
    int count, max = 0;
    bool flag = false;
    vector<char> vec_temp;
    for (int i = 0; i < len; i++){
        int ki = i;
        count = 0;
        for (int j = len - 1; j > i; j--){
            int kj = j;
            vector<char> vec;
            while (s[ki] == s[kj]){
                count++;
                vec.push_back(s[ki]);
                if (ki == kj || ki + 1 == kj)
                {   
                    if (ki == kj){
                        count = (count - 1) * 2 + 1;
                    }
                    else{
                        count = count * 2;
                    }
                    flag = true;
                    vec_temp = vec;
                    break;
                }
                ki++;
                kj--;
            }
            if (flag == true) break;
        }
        if (max < count) {
            v = vec_temp;
            max = count;
        }
    }
    cout << max << endl;
    return max;
}//分类暴力算法
string longestPalindrome(string s){
    int max1 = 0;//奇数最长子串
    int max2 = 0;//偶数最长子串
    int idx1 = 0;//奇数最长子串的中心字符
    int idx2 = 0;//偶数最长子串的中心字符
    string result;
    //计算奇数最长回文字符串
    for (int i = 0; i < s.length(); i++){

        //注意针对奇数偶数回文子串,我们有不同的策略,
        //奇数,直接在while中可以进行++z和--j操作
        //偶数,无法直接在while中进行++z和--j操作,因为需要先比较s[z]和s[j]是否相同呢

        //计算奇数最长回文字符串,注意这儿将j,z,count1,count2定义在了for循环内部,如果放在外部容易被修改出现意想不到的问题,所以需要注意定义方式
        int j = i;
        int z = i;
        int count1 = 0;
        int count2 = 0;
        while ((++z < s.length() && (--j >= 0) && s[z] == s[j])){
            count1 += 2;
            if (count1>max1){
                max1 = count1;
                idx1 = i;
            }
        }

        //计算偶数最长回文字符串
        j = i;
        z = i + 1;
        while ((z < s.length()) && (j >= 0) && (s[z] == s[j])){
            count2 += 2;
            if (count2>max2){
                max2 = count2;
                idx2 = i;
            }
            z++;
            j--;
        }
    }
    if (max1 + 1 > max2) result = s.substr(idx1 - max1 / 2, max1 + 1);
    else result = s.substr(idx2 - max2 / 2 + 1, max2);
    return result;
}//分类暴力算法优化
//通过改造字符串使得程序不用区分奇偶了
//aba改造为#a#b#a#,abba改造为#a#b#b#a#,即处理的都是奇数啦!
string longestPalindrome_better(string s){
    int max = 0;
    int idx = 0;
    string temp[2005];
    int j = 0;
    for (int i = 0; i < s.size(); i++){
        temp[j++] = '#';
        temp[j++] = s[i];
    }
    temp[j++] = '#';
    temp[j] = '\0';
    for (int i = 0; i < 2 * s.size() + 1; i++){
        int j = i;
        int z = i;
        int count = 0;

        while ((++z) < (2 * s.size() + 1) && (--j >= 0) && (temp[z] == temp[j])){
            count++;
            if (count > max){
                max = count;
                idx = i;
            }
        }
    }
    return s.substr((idx - max) / 2, max);
}
//动态规划
string longestPalindrome_DP(string s){
    int len = s.length();
    int idx = 0;
    int max = 1;
    int map[1000][1000] = { 0 };
    //初始化奇数的回文子串
    for (int i = 0; i < len; i++){
        map[i][i] = 1;
    }
    //初始化偶数的回文子串
    for (int i = 0; i < len - 1; i++){
        if (s[i] == s[i + 1]){
            map[i][i + 1] = 1;
            idx = i;
            max = 2;
        }
    }
    for (int plen = 3; plen <= len; plen++){
        for (int j = 0; j < len - plen + 1; j++){
            int z = plen + j - 1;
            if (s[j] == s[z] && map[j + 1][z - 1]){
                map[j][z] = 1;
                idx = j;
                max = plen;
            }
        }
    }
    return s.substr(idx, max);
}//马拉车
string longestPalindrome_Manacher(string s){
    string temp;
    temp += "$#";
    for (int i = 0; i < s.size(); i++){
        temp += s[i];
        temp += "#";
    }
    int *p = new int[temp.length()];
    memset(p, 0, sizeof(p));
    //max记录的是最长回文子串的长度,idx记录的是最长回文串的中心位置
    int max = 0, idx = 0;
    //mx记录i之前的最长回文子串延伸到最右边的位置,id记录该字符串的中心位置
    int mx = 0, id = 0;
    //因为第一个是'$'字符,所以不用计算它的p值
    for (int i = 1; i < temp.length(); i++){
        if (mx > i){
            p[i] = (p[2 * id - i] < (mx - i) ? p[2 * id - i] : (mx - i));
        }
        else{
            p[i] = 1;
        }
        while (i + p[i] < temp.length() && temp[i - p[i]] == temp[i + p[i]]){
            p[i]++;
        }
        if (i + p[i] > mx){
            id = i;
            mx = i + p[i];
            if (p[i] > max){
                max = p[i];
                idx = id;
            }
        }
    }
    max--;
    delete p;
    return s.substr((idx - max) / 2, max);

}
int main(){
    string s = "asdfjafhellololleh";
    string s1 = "level";
    vector<char> vec;
    string s2 = longestPalindrome(s);
    cout << s2 << endl;
    string s3=longestPalindrome_DP(s);
    cout << s3 << endl;
    string s4 = longestPalindrome_Manacher(s);
    cout << s4 << endl;
    return 0;
}

给定一个字符串s,你可以从中删除一些字符,使得剩下的串是一个回文串。如何删除才能使得回文串最长呢?也就是需要删除最少的字符个数,输出需要删除的字符个数。

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;

int helper(string s)
{
    string rev = s;
    reverse(rev.begin(), rev.end());
    int size = s.size();
    vector<vector<int>> c(size + 1, vector<int>(size + 1));
    for (int i = 1; i <= size; ++i)
        for (int j = 1; j <= size; ++j)
        {
            if (s[i - 1] == rev[j - 1])
            {
                c[i][j] = c[i - 1][j - 1] + 1;
            }
            else if (c[i-1][j] >= c[i][j-1])
            {
                c[i][j] = c[i - 1][j];
            }
            else if (c[i - 1][j] < c[i][j - 1])
            {
                c[i][j] = c[i][j - 1];
            }
        }
    //打印
    /*
    for (int i = size, j = size; i >= 1 && j >= 1;)
    {
        if (s[i - 1] == rev[j - 1])
        {
            cout << s[i - 1] << " ";
            --i;
            --j;
        }
        else if (c[i][j - 1] >= c[i - 1][j])
        {
            --j;
        }
        else if (c[i][j - 1] < c[i - 1][j])
        {
            --i;
        }
    }
    */
    return size - c[size][size];
}
int main()
{
    string s;
    while (cin >> s)
        cout << helper(s) << endl;
}

猜你喜欢

转载自blog.csdn.net/sinat_21026543/article/details/80039862