alg : 字符串按照字符进行循环移位(左旋转 or 右旋转)

题目需求:

alg : 字符串按照字符进行循环移位(左旋转 or 右旋转)_第1张图片

算法:

/// 字符串循环左移或右移N个字符的算法
/// * 将整个串全反转
/// * 按照需要移动的字符个数将反转后的串分为2部分
/// * 将这2个子字符串分别进行反转
///
/// e.g. 原串: hello world, 要求循环左移3个字符
/// * 将整个串反转, dlrow olleh
/// * 按照循环的字符数3, 将翻转后的串分为2部分, [dlrow ol][leh]
/// * 将这2个子字符串分别进行反转 [lo world][hel]
/// 得到字符串按照字符为单位循环移位的结果为 lo worldhel

测试程序效果图:

alg : 字符串按照字符进行循环移位(左旋转 or 右旋转)_第2张图片

测试程序算法实现:

// srcStringCirculator.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include <windows.h>
#include <locale.h>
#include <tchar.h>

#include "Helper\StringHelper.h"

/// 字符串循环左移或右移N个字符的算法
/// * 将整个串全反转
/// * 按照需要移动的字符个数将反转后的串分为2部分
/// * 将这2个子字符串分别进行反转
///
/// e.g. 原串: hello world, 要求循环左移3个字符
/// * 将整个串反转, dlrow olleh
/// * 按照循环的字符数3, 将翻转后的串分为2部分, [dlrow ol][leh]
/// * 将这2个子字符串分别进行反转 [lo world][hel]
/// 得到字符串按照字符为单位循环移位的结果为 lo worldhel

void DoStringCirculatorOnce();      ///< 开始一次字符串字符移位
bool IsDoStringCirculatorAgain();   ///< 是否再开始一次新的字符串字符移位

int _tmain(int argc, _TCHAR* argv[])
{
    setlocale(LC_CTYPE, ".936");    //< 控制台为中文输出

    do 
    {
        DoStringCirculatorOnce();
    } while (IsDoStringCirculatorAgain());

    _tprintf(L"\r\nEND, press any key to quit\r\n");
    getchar();

    /** run results
    */

	return 0;
}

void DoStringCirculatorOnce()
{
    wchar_t     szBuf[_MAX_PATH];                                   ///< 用户输入的原始字符串
    wchar_t *   pszBuf              =   &szBuf[0];
    size_t      nInputBufLenMax     =   SIZEOF_WCHAR_ARRAY(szBuf);
    bool        bLoopLeft           =   true;
    size_t      nLoopCnt            =   0;

    if (!GetUserInputStringCirculatorParam(pszBuf, nInputBufLenMax, bLoopLeft,nLoopCnt))
        return;

    if (StringCirculator(pszBuf, bLoopLeft,nLoopCnt))
        _tprintf(L"\r\n循环字符移位后的串为 : [%s]\n", szBuf);
    else
        _tprintf(L"\r\n操作失败\r\n");
}

bool IsDoStringCirculatorAgain()
{
    bool        bRc                     =   true;
    wchar_t     cInput                  =   L' ';       ///< 用户输入的单个字符
    wchar_t *   pcInput                 =   NULL;
    wchar_t     szloopCnt[_MAX_PATH];                   ///< 容纳用户输入的移位字符数
    wchar_t *   pszloopCnt              =   NULL;

    _tprintf(L"\r\n是否开始一次新的字符串字符移位? 输入'Y'为继续, 输入'N'为取消, 支持大小写\r\n");
    pcInput = &cInput;
    GetUsrInput(pcInput, SIZEOF_WCHAR_ARRAY(cInput));
    bRc = ((L'Y' == cInput) || (L'y' == cInput));

    _tprintf(L"\r\n\r\n您的选择为[%s]\r\n", bRc ? L"继续" : L"取消");

    return bRc;
}

/// @file           helper\StringHelper.h
/// @brief          字符串辅助操作定义

#ifndef __HELPER_STRING_HELPER_H__
#define __HELPER_STRING_HELPER_H__

#include <windows.h>
#include <locale.h>
#include <tchar.h>

#define WCHAR_STR_END   L'\0'   ///< 串结束符
#define WCHAR_WORD_END  L' '    ///< 单词结束符

#define SIZEOF_WCHAR_ARRAY(x)   (sizeof((x)) / sizeof(wchar_t))

/// @fn         ReverseStringByWord
/// @brief      对入参串进行单词反转, 单词之间的分隔符号为空格, 
///             e.g. Hello world, driver => driver world Hello
/// @param      IN OUT wchar_t * pcMsgW, 输入输出串, 反转后的结果串覆盖原字符串
/// @return     bool
/// @retval     true, 串反转成功
/// @retval     false, 串反转失败
bool    ReverseStringByWord(IN OUT wchar_t * pcMsgW);

/// @fn         ReverseString
/// @brief      对入参串进行完全反转
///             e.g. 123456 7890 => 0987 654321
/// @param      IN OUT wchar_t * pcMsgW, 输入输出串, 反转后的结果串覆盖原字符串
/// @param      IN size_t nLenMsg, 串长度
/// @return     bool
/// @retval     true, 串反转成功
/// @retval     false, 串反转失败
bool    ReverseString(IN OUT wchar_t * pcMsgW, IN size_t nLenMsg);

/// @fn         GetStringLength
/// @brief      此算法不允许用C库函数参与串长度计算, 模拟实现计算串长度
/// @param      IN const wchar_t * pcMsgW, 输入串
/// @param      IN const wchar_t & cEndSeparator, 串的结束分隔符
/// @param      IN const wchar_t & cStrEndSeparator, 字符串结尾字符
/// @return     size_t, 返回的串长度. 如果串为NULL, 返回0.
size_t  GetStringLength(IN const wchar_t * pcMsgW, 
                        IN const wchar_t & cEndSeparator, 
                        IN const wchar_t & cStrEndSeparator = WCHAR_STR_END);

/// @fn         GetUserInputStringCirculatorParam
/// @brief      请用户输入字符串, 指定左移或右移, 移位的字符数
/// @param      wchar_t *& pszUsrInput, 容纳用户输入字符的缓冲区, 外部给定, 必须有效
/// @param      size_t nInputBufLenMax, 输入缓冲区最大容量
/// @param      bool & bLoopLeft, 左移 = TRUE, 右移 = FALSE
/// @param      size_t & nLoopCnt, 移位的字符数, 可以大于字符串长度, 也可以为零,
///             最后的移位字符数为字符串实际长度(不包括结尾'\0')的模
/// @return     bool
/// @retval     true, 用户输入参数成功
/// @retval     false, 用户输入参数失败
bool    GetUserInputStringCirculatorParam(wchar_t *& pszUsrInput, 
                                          size_t nInputBufLenMax, 
                                          bool & bLoopLeft, 
                                          size_t & nLoopCnt);

/// @fn         GetUsrInput
/// @brief      给定输入缓冲区, 等待用户输入, 以回车作为结束符
/// @param      wchar_t *& pszUsrInput, 输入缓冲区
/// @param      size_t nInputBufLenMax, 输入缓冲区长度
/// @return     void
void    GetUsrInput(wchar_t *& pszUsrInput, size_t nInputBufLenMax);

/// @fn         StringCirculator
/// @brief      对输入串进行按字符数循环移位
/// @param      wchar_t *& pszUsrInput, 输入串
/// @param      bool bLoopLeft, 左移 = TRUE, 右移 = FALSE
/// @param      size_t nLoopCnt, 移位的字符数, 可以大于字符串长度, 也可以为零,
///             最后的移位字符数为字符串实际长度(不包括结尾'\0')的模
/// @return     bool
/// @retval     true, 成功
/// @retval     false, 失败
bool    StringCirculator(wchar_t *& pszUsrInput, 
                        bool bLoopLeft, 
                        size_t nLoopCnt);

#endif

/// @file           helper\StringHelper.cpp
/// @brief          字符串辅助操作, 实现串反转, 字符串移位等操作

#include "stdafx.h"
#include "StringHelper.h"

bool    ReverseStringByWord(wchar_t * pcMsgW)
{
    size_t  nLenAll     =   0;
    size_t  nPosNow     =   0;
    size_t  nPosPrev    =   0;
    size_t  nLenStr     =   0;

    if (NULL == pcMsgW)
        return false;

    /// 反转整个串
    nLenAll = GetStringLength(pcMsgW, WCHAR_STR_END);
    if (!ReverseString(pcMsgW, nLenAll))
        return false;

    /// 反转每个单词
    while (nPosPrev < (nLenAll - 1))
    {
        nLenStr = GetStringLength(pcMsgW + nPosPrev, WCHAR_WORD_END);

        /// 串长度 - 1 是位置, 位置是基于0的
        nPosNow = nPosPrev + nLenStr - 1;
        if ((nPosNow <= nPosPrev) 
            || (!ReverseString(pcMsgW + nPosPrev, nLenStr)))
            break;

        /// 下一个单词的起点是上一个单词的起点 + 一个单词分隔符长度 + 下一个单词的首字符
        nPosPrev = nPosNow + 2;
    }

    return true;
}

bool    ReverseString(IN OUT wchar_t * pcMsgW, IN size_t nLenMsg)
{
    wchar_t *   pcBegin =   NULL;
    wchar_t *   pcEnd   =   NULL;

    if (NULL == pcMsgW)
        return false;

    if (nLenMsg <= 1)
        return true;

    pcBegin = pcMsgW;
    pcEnd = pcMsgW + nLenMsg - 1;

    while (pcBegin < pcEnd)
    {
        /// 交换字符
        *pcBegin ^= *pcEnd;
        *pcEnd ^= *pcBegin;
        *pcBegin ^= *pcEnd;

        /// 移动头尾指针
        pcBegin++;
        pcEnd--;
    }

    return true;
}

size_t  GetStringLength(IN const wchar_t * pcMsgW, 
                        IN const wchar_t & cEndSeparator, 
                        IN const wchar_t & cStrEndSeparator)
{
    size_t          nLenCnt    =   0;
    const wchar_t * pcBegin =   pcMsgW;

    if (NULL == pcBegin)
        return 0;

    while ((*(pcBegin + nLenCnt) != cEndSeparator)
        && (*(pcBegin + nLenCnt) != cStrEndSeparator))
        nLenCnt++; ///< 当前字符不是cEndSeparator, 也不是字符串结尾, 计数++

    return nLenCnt;
}

bool    GetUserInputStringCirculatorParam(wchar_t *& pszUsrInput, 
                                          size_t nInputBufLenMax, 
                                          bool & bLoopLeft, 
                                          size_t & nLoopCnt)
{
    wchar_t     cInput                  =   L' ';       ///< 用户输入的单个字符
    wchar_t *   pcInput                 =   NULL;
    wchar_t     szloopCnt[_MAX_PATH];                   ///< 容纳用户输入的移位字符数
    wchar_t *   pszloopCnt              =   NULL;

    if (nInputBufLenMax < 1)
    {
        _tprintf(L"缓冲区长度无效, 必须 >= 1\r\n");
        return false;
    }

    if (NULL == pszUsrInput)
    {
        _tprintf(L"缓冲区为空\r\n");
        return false;
    }

    _tprintf(L"\r\n请输入字符串, 支持中文和分隔符, 以回车作为输入结束符:\r\n");
    ::ZeroMemory(pszUsrInput, nInputBufLenMax * sizeof(wchar_t));
    GetUsrInput(pszUsrInput, nInputBufLenMax);

    _tprintf(L"\r\n该字符串是左移还是右移? 输入'L'为左移, 输入'R'为右移, 支持大小写\r\n");
    pcInput = &cInput;
    GetUsrInput(pcInput, SIZEOF_WCHAR_ARRAY(cInput));
    bLoopLeft = ((L'L' == cInput) || (L'l' == cInput));

    _tprintf(L"\r\n请输入移位字符数, 可以为0 或大于字符串长度, 最终的移位字符数为您输入的移位字符数模字符串长度\r\n");
    ::ZeroMemory(szloopCnt, sizeof(szloopCnt));
    pszloopCnt = &szloopCnt[0];
     GetUsrInput(pszloopCnt, SIZEOF_WCHAR_ARRAY(szloopCnt));
     nLoopCnt = _ttoi64(szloopCnt);

    _tprintf(L"\r\n\r\n您输入的字符串为: [%s]\r\n", pszUsrInput);
    _tprintf(L"\r\n字符串循环字符移位操作为: %s%d个字符\r\n", bLoopLeft ? L"左移" : L"右移", nLoopCnt);
}

void    GetUsrInput(wchar_t *& pszUsrInput, size_t nInputBufLenMax)
{
    size_t      nPos    =   0;
    wchar_t     cInput  =   L' ';

    if ((NULL == pszUsrInput) || (nInputBufLenMax < 1))
        return;

    while(1)
    {
        cInput = getwchar();    ///< _tprintf_s 有问题,不支持','分隔, 采用getwchar循环接收代替
        if (L'\n' == cInput)
            break;

        if (nPos >= nInputBufLenMax)
            break;

        *(pszUsrInput + nPos++) = cInput;
    }
}

bool    StringCirculator(wchar_t *& pszUsrInput, 
                         bool bLoopLeft, 
                         size_t nLoopCnt)
{
    size_t  nLenAll         =   0;
    size_t  nLoopCntReal    =   0;

    if (NULL == pszUsrInput)
        return false;

    /// 反转整个串
    nLenAll = GetStringLength(pszUsrInput, WCHAR_STR_END);
    if (!ReverseString(pszUsrInput, nLenAll))
        return false;

    if (nLenAll < 1)
        return false;

    nLoopCntReal = nLoopCnt % nLenAll;

    if (0 == nLoopCntReal)
        return true;

    /// 反转2个子串, 串边界为 nLenAll - nLoopCntReal
    ReverseString(pszUsrInput, bLoopLeft ? (nLenAll - nLoopCntReal) : nLoopCntReal);
    ReverseString(  pszUsrInput + (bLoopLeft ? (nLenAll - nLoopCntReal) : nLoopCntReal), 
                    bLoopLeft ? nLoopCntReal : (nLenAll - nLoopCntReal));
    return true;
}



你可能感兴趣的:(alg : 字符串按照字符进行循环移位(左旋转 or 右旋转))