Reverse String

来源:互联网 发布:单片机数码管接线 编辑:程序博客网 时间:2024/06/05 16:23

Reverse String


  • Reverse String
    • 摘要
    • 题目描述与分析
    • 题目解决方案与分析
      • 方案一 直接逆序for循环
      • 方案二 字符串内首尾字符依次交换
      • 方案三 直接逆序reverse函数
      • 方案四 分治算法实现
    • 总结


摘要

本次内容主要是对LeetCode网站上算法题目分类下的Reverse String一题提供一些个人的解决方案,用于完成个人在算法设计与分析课程的学习任务。


题目描述与分析

Write a function that takes a string as input and returns the string reversed.

Example:
Given s = “hello”, return “olleh”.

题目要求为输入一个字符串,通过代码实现输出该字符串的逆序,比如输入字符串“hello”,输出结果为“olleh”。


题目解决方案与分析

方案一: 直接逆序(for循环)

实现一个字符串的逆序,可以直接将目标字符串从末端到前端的字符依次添加到一个空字符串的末端(即从后到前),最后得到的字符串就是结果字符串。该过程使用了一个for循环,代码如下:

//reverseString1.cpp-使用for循环直接逆序#include <iostream>#include <cstring>using namespace std;string reverseString(string s);int main() {    string S = "hello";    cout << reverseString(S) << endl;    return 0;}string reverseString(string s){     string N = "";     int str_len = s.length();     for (int i = str_len - 1; i >= 0; i --)      {         N += s.at(i);     }     return N;}

可以看到该方案中,程序执行时间主要消耗在for循环中,假设字符串长度为n,那么该循环体内的语句要执行n次,频度为f(n) = n,所以算法的时间复杂度为T(n) = O(n),实际程序运行时间所需为9ms。

方案二: 字符串内首尾字符依次交换

实现字符串逆序的另一种方案就是字符串的首端的字符与末端的字符依次交换,比如字符串长度为n,第1个字符与第n个字符交换,然后第2个字符与第n-1个字符交换……直到最后实现字符串后半段与前半段交换,就完成了字符串逆序。代码如下:

//reverseString2.cpp-字符串内首尾字符依次交换#include <iostream>#include <cstring>using namespace std;string reverseString(string s);int main() {    string S = "hello";    cout << reverseString(S) << endl;    return 0;}string reverseString(string s){    int str_len = s.length();    int i = 0;    int j = str_len - 1;    while (i < j)    {        swap(s[i++], s[j--]);    }    return s;}

同上,该程序的执行时间主要消耗在while循环中,假设字符串的长度为n,那么循环次数为n/2,循环体内语句的频度为f(n) = n/2,算法的时间复杂度就是T(n) = O(n)。实际程序的执行时间与方案一相同,也是9ms。

方案三: 直接逆序(reverse函数)

该方案使用了reverse函数,可以直接将目标字符串逆序,使得代码最简化,代码如下:

//reverseString.cpp-使用reverse函数直接逆序#include <iostream>#include <algorithm>using namespace std;string reverseString(string s);int main() {    string S = "hello";    cout << reverseString(S) << endl;    return 0;}string reverseString(string s){    string N = s;    reverse(N.begin(), N.end());    return N;}

reverse函数原型如下:

template<class BidirectionalIterator>void reverse(   BidirectionalIterator _First,    BidirectionalIterator _Last);

尽管代码更简洁,然后该程序的实际执行时间仍是9ms。因为reverse函数的实现其实和方案二类似,只不过实现具有一般化,而不仅限于字符串,内部实现算法的时间复杂度还是O(n)。

方案四: 分治算法实现

根据最近课程学习的内容,可以应用分治算法到该题里。

利用分治法解决问题通常需要以下三个步骤:
1. 将原问题分解为一组子问题,每个子问题都与原问题类型相同,但是比原问题的规模小;
2. 递归求解这些子问题;
3. 将子问题的求解结果恰当合并,得到原问题的解。

那么本题里,可以将字符串从中间一分为二,前半部分一组,后半部分一组,先分别逆序前半字符串和后半字符串,然后将逆序后的前半字符串添加到逆序后的后半字符串末端,就会得到逆序的字符串。当然,过程中会继续对已分的前后半两组字符串分组,直到只包含两个字符(问题分解到最小)。

比如,本题里原字符串“hello”,第一次分组,前半字符串为“he”,后半字符串为“llo”;第二次再分组,只能是对“llo”分组(因为“he”只包含两个字符了),得到“l”和“lo”,然后逆序,再组合。那么过程为:
“hello”->
“he”,“llo”->
“he”,“l”、“lo”->
“eh”,“l”、“ol”->
“eh”,“oll”->
“olleh”

所以代码如下:

//reverseString.cpp-使用分治算法实现#include <iostream>#include <cstring>using namespace std;string reverseString(string s);int main() {    string S = "hello";    cout << reverseString(S) << endl;    return 0;}string reverseString(string s){    int str_len = s.length();    if (str_len < 2)     {        return s;    }    string str_forward, str_backward;    str_forward.assign(s, 0, str_len / 2);    str_backward.assign(s, str_len / 2, str_len - (str_len / 2));    return reverseString(str_backward) + reverseString(str_forward); //递归}

其中assign函数是string类的一个成员函数,函数原型是:
string &assign(const string &s, int start, int n);
作用是把字符串s从start开始的n个字符赋给当前字符串。

分析该算法,使用了分治算法,每次分组分为两个子问题,每个子问题的规模都是原问题的一半,而且在合并字符串的时候,需要O(n)的时间,因为假设原问题的字符串长度为n,最后返回的字符串长度也是n,那么将后半字符串赋值给返回字符串并把前半字符串添加在末端时,一共需要进行n位的字符操作。所以可以得出的复杂度递推式是T(n)=2T(n/2)+O(n)。
根据定理:
大师定理
a=2,b=2,d=1,所以该算法的时间复杂度应为O(nlog n),程序实际执行时间是23ms,比上述的方案的耗时均都要长。


总结

对这道题的解答,个人目前只想到这几种在C++下的解决方案。关于更优的解决方案,会在后续博客中补充。

原创粉丝点击