【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;
    }
};

三、总结

憨憨自闭做法不可取。应用恰当的函数和数据结构,会使得问题简单许多许多。

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