《leetcode》第一阶段汇总(10-20题)(一)

前言

总结花时间蛮长的,比做题慢多了。~

10. Regular Expression Matching

算是比较难的一道DP题,会有几个地方比较难想:

  • 状态比较难确定, F [ i , j ] F[i,j] F[i,j]该怎么表示会有疑惑
  • 状态转移方程难确定,很容易写出错的转移方程
  • 初始状态难确定,初始状态又是一个子DP题。
  现在整理下自己AC版本的一些思考过程: ```c //1. 设F[i,j]代表S前i和P前j个子符子串的匹配情况 true or false, //2. 转移方程为: F[i,j]=F[i-1,j-1] if s[i-1] == p[j-1] =F[i,j-2] if p[j-1] == '*' //这里需要多想下,刚开始我是写错的 =F[i-1,j] if p[j-1] == '*' and s[i-1] == p[j-2] or p[j-2] == '.' //3. 初始状态为 F[0....m-1,m] F[0,1...,n]
  1. F[0][0]=true
  2. F[0…m,0] //的状态如下:
    for i=1 to m
    F[i,0]=false;
  3. F[0,0…n-1,n]//的状态如下:
    //重写按照一个新的dp小问题求解:
    for i=1 to n
    F[0,i]=F[0,i-1] if p[i-1] == ‘’ and F[0,i-1]
    =F[0,i-2] if i-2 >=0 and p[i-1] == '
    ’ and F[0,i-2]

  一开始我是为了不麻烦自己,设$F[i,j]$代表当前s中以i位置结尾和p中以j位置结尾的子串匹配的情况,结果到更新初始状态时候遇到了很大麻烦,在类似`"d","a*b*c*d"`的`case`中很难处理初始状态。
  改变定义为$F[i,j]$代表s前i和p前j个子符子串的匹配情况,**初始状态的更新可以当成一个新的dp问题解决。**最后再看看前面标注的信息:
```c
F[i,j]=F[i,j-2]   if p[j-1] == '*' //很容易多加 && s[i-1] != p[j-2] 而出错

这个状态代表p中x*直接跳过,**而这种直接跳过的状态只依赖p[j-1] == '*'是否成立,**这里极易出错,想不清楚。然后就是代码实现的一些技巧,省略了if判断语句简写了很多逻辑代码,这种只有在状态为布尔值时,才能实现的编程简化。

class Solution {
public:
    bool isMatch(string s, string p){
        vector<vector<int>> F(s.size()+1);
        for(int i=0;i<=s.size();++i)
            F[i].resize(p.size()+1,0);
        
        F[0][0]=true;
    
        for(int j=1;j<=p.size();++j)
            F[0][j]=F[0][j-1] && p[j-1] == '*' || j-2>=0 && F[0][j-2] && p[j-1] == '*';
    
        
        for(int i=1;i<=s.size();++i){
            for(int j=1;j<=p.size();++j){
                if(p[j-1] != '*')
                    F[i][j]=F[i-1][j-1] && (s[i-1] == p[j-1] || p[j-1] == '.');
                else
                    //&& s[i-1] != p[j-2]
                    F[i][j]=j-2>=0 && F[i][j-2]&& s[i-1] != p[j-2] || F[i-1][j] && (s[i-1] == p[j-2] ||p[j-2] == '.');
            }
        }
        return F[s.size()][p.size()]; 
    }  
};

14. Longest Common Prefix

这道题看题解之后,觉得有几个比较好的地方值得总结。首先抽象问题为 F ( S ) = F ( . . ( F ( S 1 , S 2 ) , S 3 ) . . . , S n − 1 ) F(S)=F(..(F(S1,S2),S3)...,Sn-1) F(S)=F(..(F(S1,S2),S3)...,Sn1),这对应了横向扫描的办法, L C P ( S ​ 1 ​​ … S ​ n ​​ ) = L C P ( L C P ( S ​ 1 ​​ … S ​ k ​​ ) , L C P ( S ​ k + 1 ​​ … S ​ n ​​ ) ) LCP(S​1​​…S​n​​)=LCP(LCP(S​1​​…S​k​​),LCP(S​k+1​​…S​n​​)) LCP(S​1​​Sn​​)=LCP(LCP(S​1​​Sk​​),LCP(Sk+1​​Sn​​))对应了分治的方法,最后还有的垂直的解法也是我所采用的。但是最后的扩展提高中提到了**前缀树**的解法,原文使用的java实现的前缀树,我改写的C++版本如下所示:

struct Node {
    bool containsKey(char ch) {
        return ptr[ch -'a'] != NULL;
    }
    Node * get(char ch) {
        return ptr[ch -'a'];
    }
    void put(char ch, Node * node) {
        ptr[ch -'a'] = node;
    }
    void setEnd() {
        isend = true;
    }
    Node(int x=false,int y=26) : isend(x), R(y){ ptr.resize(R,NULL);}
    int R;
    vector<Node * > ptr;
    bool isend;
};


class Solution {
public:
    void insert(string word,Node * root) {
        Node * node = root;
        for (int i = 0; i < word.length(); ++i) {
            char c = word[i];
            if (!node->containsKey(c-'a')) {
                node->put(c-'a',new Node());
            }
            node = node->get(c-'a');
        }
        //下一个节点的isend为true
        node->setEnd();
    }
    Node * searchPrefix(string word,Node * root) {
        Node * node = root;
        for (int i = 0; i < word.length(); ++i) {
           char c = word[i];
           if (!node->containsKey(c-'a')) {
               return NULL;
           }
           else 
               node = node->get(c-'a');
        }
        return node;
    }

    bool search(string word,Node * root) {
        Node * node = searchPrefix(word,root);
        return node != NULL && node->isend;
    }
    
    bool startsWith(string prefix,Node * root) {
        Node * node = searchPrefix(prefix,root);
        return node != NULL;
    }
}

至于前缀树的几个题,到以后的阶段三再处理,现在不拓展太多。

19. Remove Nth Node From End of List

这个题一般都能想到two pass的解法,我一直在想one pass的解法到底怎么做,最后题解提示:寻找从后面数第k个元素,可以用双指针的方法,两个指针一直保持k的距离,则后面一个指针到达末尾的时候,前一个指针就是第k个元素。但是为了删除第k个元素,所以需要找到第k+1个元素。我实现的c++版本如下:

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode * first=NULL,* second=NULL;
        int i;
        for(i=0,first = head;i<n-1;++i)
            first=first->next;
        if(first->next== NULL)
            return head->next; 
        for(second=head;first->next->next != NULL; first=first->next,second=second->next);
        second->next=second->next->next;
        return head;
    }
};

小结

10-20还有其他题没有总结,所以还没有结束。本篇总结了一个较复杂的dp问题,抽象问题的方式及前缀树的概念,最后学习到了one pass删除一个链表第k项(从后数)的方法。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值