题目:输入两个字符串,比如 abdbcc (source string) 和 abc (target string),输出第二个字符串在第一个字符串中的连接次序。即输出125、126、145、146.
方法一:递归方法,总结理解书(面试宝典)上的代码
重要的点:需要声明一个和目标字符串长度相同的 index 数组 (printArray[]) 用于存放满足条件的连接次序,然后递归比较,满足条件时打印或者存放结果于指定数组中。
分析:对于这两个字符串,第一个字符 a 在位置 0 都相等,所以问题递归转化为在字符串 bdbcc 中找 bc 的连接顺序,然后进一步转化为在 dbcc 中找 c 的连接顺序,依次递归 ...... 。
如上图,第一轮两个字符串都是从位置 0 开始比较,发现 TargetStr[0] = SourceStr[0], TargetStr[1] = SourceStr[1], 然后继续在 SourceStr 中找和 TargetStr[2] 相同的元素,发现有两个位置满足。
然后对于字符 Target[1],继续从上次满足条件的下一位置往后继续查找满足的位置,若找到,继续重复查找 Target[2] 在 SourceStr 中的位置,如此依次向前递归。最终结果如下图
代码如下:
/**************************** author: Charleey@2017/07/02 ***************************
* iTargetStr: 目标字符串
* iSourceStr: 源字符串
* ipPrintArray: 满足条件的 index 数组,长度和 iTargetStr 相同
* iPrintArrayLen:标识对于 ipPrintArray 已经填充到第几位
* tStartPos: 目标字符串的起始比较位置
* sStartPos: 源字符串的起始比较位置
* ioResultIndex: 存储最满足条件的 index 集合
**********************************************************************************/
void PrintfArray(const string& iTargetStr, const string& iSourceStr, int *ipPrintArray, int iPrintArrayLen, int tStartPos, int sStartPos, vector<vector<int>>& ioResultIndex)
{
if (iPrintArrayLen == iTargetStr.length()) //若打印的数组中已经填充到最后一位,说明一轮查找已经结束
{
vector<int> lOneResultIndex;
for (int i = 0; i < iPrintArrayLen; ++i)
lOneResultIndex.push_back(*(ipPrintArray + i));
ioResultIndex.push_back(lOneResultIndex);
return;
}
for (int i = sStartPos; i < iSourceStr.length(); ++i) //外层循环确保对于每一个 target string 中的字符都能比较到最后一个位置
{
for (int j = tStartPos; j < iTargetStr.length(); ++j)
{
if (iSourceStr[i] == iTargetStr[j]) //对于 target string 中的一个字符,若找到在 source string 中的位置,
{ //二者都从下一个位置 (i+1, j+1) 开始继续重复上述比较
ipPrintArray[iPrintArrayLen] = i + 1;
PrintfArray(iTargetStr, iSourceStr, ipPrintArray, iPrintArrayLen + 1, j + 1, i + 1, ioResultIndex);
}
}
}
}
方法二:思想仍然来源于书(面试宝典)。
分析:首先得计算出第二个字符串各个字符在第一个字符串中出现的位置,利用其位置构造多叉树(构造规则是子节点的 index 必须大于父节点的 index),然后遍历多叉树即完成相应的组合。
因为思路比较直观,直接贴出代码,如下:
/**************************** author: Charleey@2017/07/02 ***************************
* iTargetStr: 目标字符串
* iSourceStr: 源字符串
* lResultVec: 存储目标字符串中的每一字符在源字符串中的位置
**********************************************************************************/
void FindEachCharPosition(const string& iTargetStr, const string& iSourceStr, vector<vector<int>>& lResultVec)
{
if (iSourceStr.size() >= iTargetStr.size())
{
vector<vector<int>> lTempVec;
for (size_t i = 0; i < iTargetStr.size(); ++i)
{
vector<int> lEachCharactor;
for (size_t j = 0; j < iSourceStr.size(); ++j)
{
if (iTargetStr[i] == iSourceStr[j])
lEachCharactor.push_back(j + 1);
}
lTempVec.push_back(lEachCharactor);
}
if (lTempVec.size() == iTargetStr.size())
for (auto lEachIndexVec : lTempVec)
lResultVec.push_back(lEachIndexVec);
}
}
/**************************** author: Charleey@2017/07/02 ***************************
* iIndex: 标识上述图中多叉树的第几层
* iData: 目标字符串中的每一字符在源字符串中的位置
* iResult: 每一轮遍历的结果
* ioResultIndex:存储最满足条件的 index 集合
**********************************************************************************/
void RecursivelyPrint(int iIndex, vector<vector<int>>& iData, vector<int>& iResult, vector<vector<int>>& ioResultIndex)
{
if (iIndex == iData.size()) //一种组合遍历结束
{
ioResultIndex.push_back(iResult);
return;
}
for (auto it : iData[iIndex])
{
if (0 == iIndex) //多叉树的第一层,直接存储
{
iResult.push_back(it);
RecursivelyPrint(iIndex + 1, iData, iResult, ioResultIndex);
iResult.pop_back();
}
else
{
if (it > iResult[iIndex - 1]) //若不是第一层,需要判断子节点的 index 是否大于父节点的 index, 只有大于的时候才能存储
{ //因为该位置被上一层中的字符占用的话,当前层的字符不能使用
iResult.push_back(it);
RecursivelyPrint(iIndex + 1, iData, iResult, ioResultIndex);
iResult.pop_back(); //每次一轮结束后,该位置的 index 要 pop 出去,然后新一轮写入的 index 才有意义
}
}
}
}