【LeetCode】 71.Simplify Path 文件路徑化簡

一、概述

輸入一個字符串,字符串中包含“/ . a~z”,組成一個文件路徑。將其化爲最簡路徑。

有以下幾條要注意

有“/..”時,相當於返回上一層,它前面的一層要刪掉;

有“/.”時,相當於在本層,沒卵用,可以刪掉;

多個/和一個一樣,可以壓縮。

看上去很簡單,實際很麻煩,有許多邊界條件。

二、分析

1、我的方法

有邊界條件沒找出,沒AC,而且代碼十分醜陋。實在是懶得再去debug了。

class Solution {
public:
    string simplifyPath(string path) {
        //return "";
        if(path=="")
            return "";
        //if(path=="/")
            //return "/";
        string pure;
        for(int i=0;i<path.size();i++)
        {
            if(path[i]=='/')
            {
                pure+=path[i];
                while(i<path.size()&&path[i]=='/')
                    i++;
                i--;
            }
            else
                pure+=path[i];
        }
        cout<<pure<<'\n';
        if(pure=="/")
            return "/";
        if(pure[pure.size()-1]=='/')
            pure.erase(pure.size()-1);
        string pure_real0,pure_real;
        for(int i=0;i<pure.size()-2;i++)
        {
            if(pure[i]=='/'&&pure[i+1]=='.'&&pure[i+2]=='/')
            {
                i++;
            }
            else
                pure_real0+=pure[i];
        }
        if(!((pure[pure.size()-2]=='/'&&pure[pure.size()-1]=='.')))
            pure_real0=pure_real0+pure[pure.size()-2]+pure[pure.size()-1];
        if(pure_real0[pure_real0.size()-1]=='/')
            pure_real0.erase(pure_real0.size()-1);
        cout<<pure_real0<<'\n';
        for(int i=0;i<pure_real0.size()-3;i++)
        {
            if(i<pure_real0.size()-3&&pure_real0[i]=='/'
               &&pure_real0[i+1]=='.'&&pure_real0[i+2]=='.'&&pure_real0[i+3]=='/')
            {
                i=i+2;
                pure_real+='!';
            }
            else
                pure_real+=pure_real0[i];
        }
        if(pure_real[pure_real.size()-1]!='!')
      if(pure_real0[pure_real0.size()-3]=='/'&&pure_real0[pure_real0.size()-2]=='.'
         &&pure_real0[pure_real0.size()-1]=='.')
            pure_real+='!';
        else
            pure_real=pure_real+pure_real0[pure_real0.size()-3]
            +pure_real0[pure_real0.size()-2]+pure_real0[pure_real0.size()-1];
        else
        {
            
        }
        if(pure_real[pure_real.size()-1]=='/')
            pure_real.erase(pure_real.size()-1); 
        cout<<pure_real<<'\n';
        for(int i=0;i<pure_real.size();i++)
        {
            if(pure_real[i]=='!')
            {
                int j=i-1;
                while(j>=0&&pure_real[j]!='/')
                {
                    pure_real[j]='#';
                    j--;
                }
                if(j>=0&&pure_real[j]=='/')
                    pure_real[j]='#';
            }
        }
        cout<<pure_real<<'\n';
        string res;
        for(int i=0;i<pure_real.size();i++)
        {
            if(pure_real[i]!='#'&&pure_real[i]!='!')
                res+=pure_real[i];
        }
        if(res.size()==0)
            return "/";
        if(res[0]!='/')
            res='/'+res;
        return res;
    }
};

中心思想就是先對“/”進行壓縮,把多個重複的“/”壓縮成一個。然後對“/.”進行處理,注意只有當“/./”的時候纔可以把“/.”刪除。最後對“/..”進行處理。爲了便於處理,我先將“/../”變爲“*/”,然後再次遍歷,遇到*就向前刪除直到遇到/,這實在太麻煩了。

最主要的細節處理不當都存在字符串的末尾。由於我判斷“/.”實際上要判斷/./”,要判斷“/..”實際上是判斷“/../”,那麼最後幾個字符就需要單拿出來判斷,這很容易出現沒有料到的情況,總是如同打地鼠一樣,按下葫蘆起了瓢,就很煩躁。debug了半天搞得我道心不穩,而且從這裏也什麼都學不到——然後就去看討論區了。

2、較好的代碼

該代碼中用了我平時很少使用的getline函數和棧結構,從而很容易的完成了該任務。

getline函數最多有三個參數:第一個參數是istream&類型,稱爲輸入數據流,簡而言之就是輸入的字符串;第二個參數是string&類型,輸入數據流會把輸入寫入這個參數;第三個參數是char,截斷字符,函數在輸入數據流中遇到該字符就停止寫入。

要想使用該函數,我們要對輸入的path做一點變化:題目中輸入的是string,而getline要的是istream&,那麼我們要將string變爲istream,怎麼變呢?如下:

stringstream ss(path);

很簡單,這樣ss就是path的istream類型了。

然後注意一點,如果getline中輸入流中有多個截斷字符,那麼循環調用getline時,效果就是每次把兩個截斷字符中間的字符串賦值給輸入,截斷字符本身不會被寫入,同時截斷字符在每讀一次之後都會變短,賭讀一次就少一點。

那麼好了,我們把“/”作爲截斷字符,那麼一次又一次調用,就相當於把輸入流分割成了一個一個小塊,然後怎麼處理呢?

對於具有回溯特性的問題,使用棧是一個好選擇。什麼叫回溯特性呢?

就是我發現了某一個條件,該條件需要“從當前向前追溯,直到滿足另一個條件”,類似本題中的遇到“/..”就刪除前一層,這就是回溯特性。那麼如何實現呢?

建一個元素爲字符串的棧,判斷每次讀入的數據,要是“.”就不壓入,要是“..”就彈出一個,要都不是就壓入。注意彈出前判斷棧是否爲空。

這樣就很簡單。當輸入流被讀完,棧中剩下的就是最後要輸出的元素。然後依次彈出,加入“/”即可。

代碼如下:

class Solution {
public:
    string simplifyPath(string path) {
        string result="", token;
        stringstream ss(path);
        vector<string> tokens;
        while(getline(ss, token, '/')){
            if(token=="." || token=="") continue;
            else if(token==".."){
                if(tokens.size()!=0)  tokens.pop_back();
            }
            else tokens.push_back(token);
        }
        if(tokens.size()==0) return "/";
        for(int i=0; i<tokens.size(); ++i)
            result=result+'/'+tokens[i];
        return result;
    }
};

三、總結

憨憨自閉做法不可取。應用恰當的函數和數據結構,會使得問題簡單許多許多。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章