距離上次寫博客已經快一個月了,最近這個月都一直在忙着找工作,現在暫時有了一個offer,剛好昨天去網易筆試了一下,就將裏面的題目拿出來做做。
題目是這樣子的:輸入一個字符串src,從這個字符串去除頭或者尾一個字符,並將這個字符加到另外一個字符串result的尾端,刪除了src的所有字符後,要求得到最後的字符串result最小(即要求字符串是生成的字符串是按字符字典最小的字符串),例如輸入 src="acdebcb",輸出爲result="abcbcde"。
其實題目的意思就是前後不斷地取src字符串的字符元素,小的那個先取。題目不是很難,關鍵步驟是看怎麼去處理當前後字符相等時的情況。(剛開始以爲自己的思路是正確的,結果在寫這篇博客分析的時候,卻發現邏輯有點不對,於是又重新想了下,看來寫博客也有這方面的好處啊)
我的思路是這樣子的,先設置兩個下標變量front和把back分別指向字符串的頭和尾,如下圖所示:
看src[front]和src[back]哪個小,小的那個就將其加到result中,並且下標變量更新。
如果相等,就再設置兩個臨時變量left和right,分別指向相等的前後兩個元素,並調用函數去判斷是從字符串的左邊還是右邊去取數。一開始以爲要用遞歸去判斷多重相等的情況,所以就寫成函數的形式,後面調試的時候發現是不用遞歸的,結果後面還是保留了函數的形式,不過這樣子看也不錯,能把邏輯分開來,好懂一些。
函數 findIndex 是個邏輯判斷函數,首先是處理下標left和right,front和back 的越界情況,然後是多個判斷情況。
1.判斷下兩個元素,即left和right對應的元素是不是比前面的元素都大,是的話,就返回左邊或者右邊都可以,這裏是返回左邊,記得要將left值要減一。
2.判斷如果src[left]不等於src[right]且有一個小於它對應的前一位的值,那麼就返回小的值的那一端。
3.判斷如果src[left]等於src[right]且它們小於它的前一位值,那麼就循環更新left和right的值。當循環結束時,要考慮是哪個條件導致了循環結束,於是又分爲了下面幾種情況:
(1)如果是下標條件越界的,就就隨便返回一邊,這裏是返回左邊,記得將left減一,因爲之前是越界了。
(2)這裏說明沒有越界,然後判斷左右是不是相等,如果有小的一端,那麼就返回小的那個方向,如果沒有小的一端,說明這兩個元素相等而且大於前面的元素,那麼情況就和前面的情況1是一樣的,直接返回一邊即可(下標要處理)。
到這裏好像就討論完了,其實還要處理最後一個當left等於right的時候,那麼也要將最後一個元素加到字符串的後面去,那麼現在就是真正的結束了。
下面是代碼,代碼裏面有註釋,對應了上面的的分析。可以參照着代碼和分析一起看,這樣可能容易懂一些。測試了多個用例,感覺是對的,但不保證完全沒有錯誤。另外,這是網易筆試題第一道編程題,感覺是不是應該有更簡單的分析方法呢,如果大家有的話歡迎來交流。
//返回值 0代表左邊,1代表右邊
int findIndex(const std::string& src,int& front,int& back,int& left,int& right)
{
if (front > back || left > right)
{
return 0;
}
//如果下兩個元素都比前面的元素大,那麼前面的兩個元素加到目標字符串的尾部
if (left < right && src[left] > src[front] && src[right] > src[back])
{
left--;
return 0;
}
//如果left和right不相等,而且有一個小於front的元素,那麼就將小的那一端的兩個元素加到目標字符串尾部
if (left < right && (src[left] < src[front] || src[right] < src[back]) && src[left] != src[right])
{
if (src[left] < src[right])
{
return 0;
}
else if (src[left] > src[right])
{
return 1;
}
}
//如果left和right一直相等而且都是小於前一個數的話,就一直循環,直到下標結束條件或者left和right對應的元素值大於前一個值
while(left < right && src[left] == src[right] && src[left] < src[left-1])
{
left++;
right--;
}
//判斷是不是下標條件結束的
if (left >= right)
{
left--;
return 0;
}
else
{
//判斷左右是不是還相等
if (src[left] < src[right])
{
return 0;
}
else if(src[left] > src[right])
{
return 1;
}
else
{
//左右還相等的話,到這裏,就說明 src[left] >= src[left-1] && src[left] == src[right],
//那麼就遞歸求值
/*left++;
right--;
return findIndex(src,front,back,left,right);*/
//不用遞歸,直接返回之前的下標即可
left--;
return 0;
}
}
}
std::string ResortString(const std::string src)
{
string result;
int len = src.length();
if (len < 1)
{
return result;
}
else if (1 == len)
{
result = src;
return result;
}
int front = 0,back = len-1;
//兩個下標分別標記字符串的頭部和尾部
while (front < back)
{
//小的那個字符加到結果字符串中
if (src[front] < src[back])
{
result += src[front++];
}
else if (src[front] > src[back])
{
result += src[back--];
}
else
{
//如果兩個元素相等,則另外設兩個標誌下標
int left = front +1;
int right = back -1;
//調用函數去判斷是從左邊還是右邊取數
int direct = findIndex(src,front,back,left,right);
if (!direct)
{
//左邊取數
while(front <= left)
{
result += src[front++];
}
}
else
{
//右邊取數
while(back >= right)
{
result += src[back--];
}
}
}
}
//考慮下標相等的情況
if (front == back)
{
result += src[front];
}
return result;
}
//
int main()
{
string str = "acdebcb"; //acbdegfadbcb aedcezecdea baezeab acbdadbcb
cout<<ResortString(str)<<endl;
cout<<"end"<<endl;
}