• 2015学校的书面总结招募书面采访蓝汛通信算法


    一个、升序矩阵问题

    问题叙述性说明:例如,看到下面的数字,在一个m*n矩阵中,每一行都依照从左到右递增的顺序排序。每一列都依照从上到下递增的顺序排序。请完毕一个函数,按从小到大的顺序打印这个矩阵到一个一维数组中。

    如图。则应打印出1,2,2,4,4,6,7,8,8,9,9,10,11,12,13,15.



    二、无反复的最长子串问题(引用luxiaoxun博客)

    问题描写叙述:找到一个字符串中的一个连续子串,这个子串内不能有不论什么两个字符是同样的。而且这个子串是符合要求的最长的。比如输入"abcbef",输出"cbef"。

    问题求解:方法一:穷举法,使用2重外循环遍历全部的区间,用2重内循环检验子串是否符合“无反复字符”这一要求。

    当中外层循环i、j 遍历全部的下标,m、n是内层循环,检查区间[i,j]是否符合要求。

    空间复杂度是O(1),时间复杂度O(N^4)。

    //O(N^4)的时间复杂度  
    int max_unique_substring1(char * str)  
    {  
        int maxlen = 0;  
        int begin = 0;  
        int n = strlen(str);  
        for(int i=0; i<n; ++i)  
            for(int j=1; j<n; ++j)  
            {  
                int flag = 0;  
                for(int m=i; m<=j; ++m)  
                {  
                    for(int n=m+1; n<j; ++n)  
                    {  
                        if(str[n] == str[m])  
                        {  
                            flag = 1;  
                            break;  
                        }  
                    }  
                    if(flag == 1) break;  
                }  
                if(flag==0 && j-i+1>maxlen)  
                {  
                    maxlen = j-i+1;  
                    begin = i;  
                }  
            }  
        printf("%.*s
    ", maxlen, &str[begin]);  
        return maxlen;  
    }  
    方法二:对方法一的检验子串是否“无反复字符”进行改进,使用hash表记录字符是否出现过。
    //O(N^2)的时间复杂度  
    int max_unique_substring2(char * str)   
    {  
        int i,j;  
        int begin;  
        int maxlen = 0;  
        int hash[256];  
        int n = strlen(str);  
        for(i=0; i<n; ++i)  
        {  
            memset(hash,0,sizeof(hash));   
            hash[str[i]] = 1;  
            for(j=i+1; j<n; ++j)  
            {  
                if(hash[str[j]] == 0)  
                    hash[str[j]] = 1;  
                else  
                    break;  
            }  
            if(j-i > maxlen)  
            {  
                maxlen = j-i;  
                begin = i;  
            }  
        }  
        printf("%.*s
    ", maxlen, &str[begin]);  
        return maxlen;  
    }  
    方法三:对于字符串"axbdebpqawuva",构造下表:


    表中。字符串有3个‘a’。有2个‘b’,其余为单一字符。next[]记录了下一个与之反复的字符的位置,如str[0]=str[8]=str[12]=‘a’,这时next[0]=8。next[8]=12,next[12]=13,其余同理。

    值得注意的是。对于没有反复字符的。next[]存储字符结束符‘’的下标,即13。
    这里,first[i]表示i之后,第一次出现反复字符的那个位置。

    比如。str[0]之后。第一次出现的反复字符是str[5]=‘b’,当然,从str[1],str[2]開始也是一样。而从str[3]開始。要到str[12]才出现反复字符‘a’。

    能够证明。从str[i]起的最长符合要求的长度为first[i]-i。区间为[i,first[i]-1]由此得解。上述最长串是当i=3时。first[i]-i=12-3=9。

    结果串为debpqawuv。


    //O(N)的时间复杂度  
    int max_unique_substring3(char * str)   
    {  
        int maxlen = 0;  
        int begin = 0;  
        int n = strlen(str);  
        int * next = (int*)malloc(sizeof(int)*n); //next[i]记录了下一个与str[i]反复的字符的位置  
        int * first = (int*)malloc(sizeof(int)*(n+1)); //first[i]记录str[i]后面近期的一个反复点  
        int hash[256];  
        memset(hash,n,sizeof(hash));  
      
        first[n] = n;  
        for(int i=n-1; i>=0; i--)  
        {  
            next[i] = hash[str[i]];  
            hash[str[i]] = i;  
            if (next[i] < first[i+1])  
                first[i] = next[i];  
            else  
                first[i] = first[i+1]; //生成first[]表。复杂度是O(N)的  
        }  
        for(int i=0; i<n; i++)  
        {  
            if (first[i]-i > maxlen)  
            {  
                maxlen = first[i]-i;  
                begin = i;  
            }  
        }  
        free(first);  
        free(next);  
        printf("%.*s
    ", maxlen, &str[begin]);  
        return maxlen;  
    }  
    方法四:使用后缀数组

    对这个字符串构造后缀数组。在每一个后缀数组中,寻找没有反复字符的最长前缀,最长的前缀就是要找的子串

    //得到字符串最长的无反复的前缀长度  
    int longestlen(char * p)  
    {  
        int hash[256];  
        int len = 0;  
        memset(hash,0,sizeof(hash));  
        while (*p && !hash[*p])  
        {  
            hash[*p] = 1;  
            ++ len;  
            ++ p;  
        }  
        return len;  
    }  
      
    //使用后缀数组解法  
    int max_unique_substring4(char * str)  
    {  
        int maxlen = -1;  
        int begin = 0;  
        char *a[99999];  
        int n = 0;  
        while(*str != '')  
        {  
            a[n++] = str++;  
        }  
        for (int i=0; i<n; i++)  
        {  
            int temlen = longestlen(a[i]);  
            if (temlen > maxlen)  
            {  
                maxlen = temlen;  
                begin = i;  
            }  
        }  
        printf("%.*s
    ", maxlen, a[begin]);  
        return maxlen;  
    }  


    方法五:DP方案等參考http://www.ahathinking.com/archives/123.html

    三、字符串交错问题

    问题描写叙述:推断字符串c是否是字符串a和字符串b按顺序的交错(interleave)。


    什么叫interleave?是将两个字符串完整的交错在一起,拆分开以后还会是原来的a和b串。

    问题分析:一个直观的错觉:把C字符串中的A挑出来。剩下的部分假设是B,这样就说明C是interleave。


    这样会遇到一个错误,比方a="aa", b="ab", c="aaba"。

    假设从c中把A挑出来。会直接挑出開始的"aa",然后剩余的部分"ba"不等于b字符串,就会做出“不是interleave”的错误推断。这种错误是因为a、b两个字符串中有共同拥有的字符所导致的挑拣时的错误。


    情况一:假设输入字符串a、b中没有共同拥有的字符。那么各挑各的,挑到就一定是属于自己的。

    假设挑完之后c还有剩余、或者挑不完c就没了,说明c不是interleave。


    情况二:假设输入字符串a、b中是有共同拥有字符的,那么各挑各的,非常有可能发生挑选错误,把不该挑走的挑走。想要避免这个错误,当遇到共同拥有字符时(即,双方都能要的字符)。分别把两种情况都考虑。
    以下是解决这个问题的两种方向。


    1、递归方法:由外向里(由大向小)解决这个问题。
    2、DP方法:由里向外(由小向大)解决这个问题。


    递归子问题方法:
    对于目标问题(s1, s2, s3)来说,递归子问题是(s1.substr(1), s2, s3.substr(1))和(s1, s2.substr(1), s3.substr(1))两个。

    class Solution {  
    public:  
        bool isInterleave(string s1, string s2, string s3) {  
            bool r1 = false, r2 = false;  
              
            if(s1 == "" && s2 == "" && s3 == "")  
                return true;  
            if(s1 != "" && s3 != "" && s1[0] == s3[0])  
                r1 = isInterleave(s1.substr(1), s2, s3.substr(1));  
            if(s2 != "" && s3 != "" && s2[0] == s3[0])  
                r2 = isInterleave(s1, s2.substr(1), s3.substr(1));  
            return r1 || r2;  
        }  
    };  
    动态规划方法:
    国际惯例。首先找子问题,设计子问题状态量。
    对于目标问题(s1, s2, s3)来说。子问题是(s1的前i个字符,s2的前j个字符,s3的前i+j个字符)。
    设子问题状态量C[i][j],其表示,问题(s1的前i个字符。s2的前j个字符,s3的前i+j个字符)的状态(true或false)。

    那么C[s1.length()][s2.length()]就是目标问题的状态。
    初始问题状态C[0][0]=true。
    状态递推关系为: C[i][j] = ( (s1[i-1] == s3[i+j-1]) && C[i-1][j] ) || ( (s2[j-1] == s3[i+j-1]) && C[i][j-1] )
    代码:

    class Solution {  
    public:  
    bool isInterleave(string s1, string s2, string s3) {  
        size_t len1, len2;  
        len1 = s1.length();  
        len2 = s2.length();  
        if(len1 + len2 != s3.length())  
            return false;  
          
        bool **C = new bool*[len1+1]; //注意*在中间  
        for(int i=0;i<len1+1;i++)  
            C[i] = new bool[len2+1];  
          
        C[0][0] = true;  
        for(int i=1;i<len1+1;i++)  
            C[i][0] = (s1[i-1] == s3[i-1]) && C[i-1][0];  
          
        for(int j=1;j<len2+1;j++)  
            C[0][j] = (s2[j-1] == s3[j-1]) && C[0][j-1];  
          
        for(int i=1;i<len1+1;i++)  
            for(int j=1;j<len2+1;j++)  
                C[i][j] = ( (s1[i-1] == s3[i+j-1]) && C[i-1][j] ) || ( (s2[j-1] == s3[i+j-1]) && C[i][j-1] );  
          
        bool result = C[len1][len2];  
          
        for(int i=0;i<len1+1;i++)  
            delete []C[i];  
        delete []C;  
        return result;  
    }  
      
    };  
    DP方法的时间复杂度O(mn),空间复杂度O(mn)。

    四、统计某时间段内在线用户数问题

    问题描写叙述:统计指定时间段内每一个时间点有多少个用户.时间段是開始时间 00:00 结束时间 08:00
    比方一个用户 A 的记录表示为User:login_time:logout_time

    表中演示样例数据
      User            login_time       logout_time

     A                    05:57           09:01
    B                     07:13           08:00
    C                     05:00          07:00

    最后记录为[5,7),2  [7,8),2 [8,9),1 而不是[5,6),2  [6,7),2  [7,8),2 [8,9),1

    (待续)

    版权声明:本文博客原创文章。博客,未经同意,不得转载。

  • 相关阅读:
    布局
    JS基础回顾_滚动条
    JS基础回顾_Dom
    JS语法_其他
    JS语法_类型
    一些免费的API
    CSS特效(一)
    博客园在Markdown中使用JS
    C# 聊一聊屏保的设置 第一步 注册表
    2019 力扣杯-全国高校春季编程大赛 最长重复子串
  • 原文地址:https://www.cnblogs.com/hrhguanli/p/4711842.html
Copyright © 2020-2023  润新知