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