leetcode题解

刷题一时爽,一直刷题一直爽~

整理这几天刷的有关leetcode字符串部分的题解。

5. Longest Palindromic Substring

题意:清晰明了,给定一个字符串,求最长回文字串。

解题过程:开局除了暴力想不出其他的解法(扣脚…),但下一秒突然想到了kmp算法中的next数组,总觉得很像,next数组是求最长、相等的真前缀和真后缀,这个是求最长、中心对称相等的字串,然后搜了一下,有一种算法是马拉车算法(Manacher),能奇迹般的把时间复杂度降到线性

Manacher算法
1、几个名词,p,C,R:
p:由于是O(n)阶的算法,所以很明显,这个是用来遍历一遍字符串的。
C:这个是回文字符的对称中心。
R:这个是回文字符的右边界。

2、字符的预处理:
回文字符存在奇回文和偶回文。奇回文还好说,是关于某一个字符对称,而偶回文则不存在对称字符,为了能统一处理方便些,采取两个两个字符之间加一个#号的操作(这个操作也是很强的…),这样不管是奇回文还是偶回文都会关于某一个字符对称,如 “aba” 处理为 “#a#b#a#”。

3、求最大回文长度数组:
和kmp算法类似,Manacher算法之所以能线性阶,也在于他有一个能缩减时间的数组。把每次遍历到的元素的最长回文长度或者是回文半径记下来,那么他为啥能快一点捏?答案就在下面啦~

4、算法的过程:
从0开始遍历字符串,根据R(即最右回文右边界)和对称中心C来计算当前位置的最长回文半径。

然后在遍历的时候会发生两种情况:
p在R的左边 || p在R的右边
【1】
p在R的左边即最长回文字符包含当前位置,这个直接暴力拓展回文串就可以了。
【2】
第二种情况又分三种情况——
由于最长回文右边界和左边界包含着当前位置,那么在求最长回文半径的时候直接对称过去,根据对称过去的p’来判断。
1)p’位置的回文半径长度在R‘里面——直接暴力拓展
2)p’位置的回文半径长度在R‘上——那么这就是当前的值,无法再往两边拓展(由于最右回文边界已经确定,如果你仍然能往两边拓展,说明最右回文半径有误)
3)p’位置的回文半径长度在R’左边——此时应该截取一部分下来,这个要画图理解一下

代码:

#include <iostream>
#include <cstring>

using namespace std;

int main()
{
    char s[1005],s1[2010];     //原字串以及预处理的字串
    int r[2010]={-1,};    //回文半径数组
    int i,R = -1,C = -1,len;   //最右回文右边界及对称中心
    cin.getline(s,1005);
    len = strlen(s);
    for (i = 0 ; i < 2*len ; i += 2) //初始化拓展序列
        s1[i] = '#',s1[i+1]= s[i/2]; s1[i] = '#';
    int ans = -1;
    for (i = 0; i <= 2*len ; i++) 
    {
        r[i] = R > i ? min(r[2*C-i],R-i+1) : 1 ;
        while(i+r[i] <= 2*len && i - r[i] > -1)
        {
            if(s1[i-r[i]] != s1[i+r[i]])  break;
            radius[i] ++;
        }
        if(i + r[i] > R)
        {
            R = i + r[i]-1;
            C = i;
        }
        ans =  max(ans,r[i]); //更新最大值
    }
    cout<<ans-1<<endl;
    return 0;
}

6. ZigZag Conversion

题意:这个看样例大概能晓得。给出一串字符串,它是以z字形的顺序写出来的,我们要恢复它,一行一行的输出。
解题过程:我认真的数规律…果然还是太菜了…这样的话就要遍历newRow次,但是可以直接开一个字符数组,然后就只用便利一遍,空间换时间。
我的AC代码

   string convert(string s, int numRows) {
        //要求的是一行一行输出N形的图形
        string a;
        int k=0;
        int group = numRows*2 - 2;
        if(group==0)
            return s;
        int t = 0;
        //这个是分组,然后除以组数余一就是第一行输出的数
        while(t<numRows)
        {
            for(int i=0; i<s.size(); i++)
            {
                if((i+t)%group==0||((i-t)%group==0&&(i-t)>=0))
                    a += s[i];
            }
            t++;
        }
        //cout << a << endl;
        return a;
    }

14. Longest Common Prefix

题意:给一个数组,数组中的每个元素都是字符串,求出所有字符串的最长公共前缀。
解题过程:还有比这个更简单的题嘛~直接找就好了,开一个字符串记下公共前缀,然后先把第一个字符赋值给他,然后开始比较。
我的AC代码:

string longestCommonPrefix(vector<string>& strs) {
    //找最长公共前缀
    string prefix;
    if(strs.size() == 0)    //空的容器
        return "";
    prefix = strs[0];   //假设最长公共前缀是第一个
    for(int i=1; i<strs.size() && prefix.size()>0; i++) //以免出现某一个字符串是空的时候仍在比较
    {
        //首先要保证prefix的长度要比遍历到的
        if(prefix.size() < strs[i].size())
            prefix = prefix.substr(0, strs[i].size());
        for(int j=0; j<prefix.size(); j++)
            if(strs[i][j] != prefix[j])
            {
                prefix = prefix.substr(0, j);
                break;
            }
    }
    return prefix;
}

28. Implement strStr()

题意:给两个字符串,然后找出第二个字符串在第一个字符串第一次出现的位置
解题过程:妈耶完全的模板题,直接套kmp算法
我的AC代码

class Solution {
public:
int next[100000];
void getnext(string needle)
{
    //先对模式求next数组
    int len = needle.size();
    int  k=-1, i;
    next[0] = -1;
    i = 0;
    while(i<len)
    {
        if(k==-1 || needle[i]==needle[k])
        {
            i++;
            k++;
            next[i] = k;
        }
        else
            k = next[k];
    }
}

int strStr(string haystack, string needle) {
    //kmp算法
    int len2=needle.size(), len1=haystack.size();
    getnext(needle);
    //开始匹配
    int i = 0, j = 0;
    while(i < len1 && j < len2)
    {
        if(j==-1 || haystack[i] == needle[j])
        {
            ++i;
            ++j;
        }
        else
            j = next[j];
    }
    if(j == len2)   //说明遍历到最后了
        return i-j;
    else
        return -1;
}
};

借这道题也认真回顾了一下kmp算法,大师级别的算法棒棒的~

58. Length of Last Word

题意:求最后一个单词的长度。单词和单词之间都是用空格分割的,若存在则返回长度。不存在则返回-1.
解题过程:一定要记得从右往左开始扫描字符,基本操作基本操作。而且!一定要看懂题再去做,不然太浪费时间了,题目强调的是以空格分隔单词,所以其实完全不用去考虑单词内部是一个啥情况,直接去找空格就可以了。
我的wa代码

class Solution {
public:
int lengthOfLastWord(string s) {
    int k,len,i,j;
    //求最后一个单词的长度,肯定是从右往左哦,别犯傻哈
    len = s.size();
    if(len==0)
        return 0;
    for(i=len; i>=0; i--)
    {
        if((s[i]>='A' && s[i]<='Z') || (s[i]>='a' && s[i]<='z'))
            break;
    }
    if(i==0)
        return 1;
    for(j=i-1; j>=0; j--)
    {
        //开始遍历到不是字母的部分
        if((s[j]<'A' || s[j]>'z') || (s[j]>'Z' && s[j]<'a'))
            break;
    }
    return (i-j);
}
};

千万不要想太多了,没有这么复杂,直接与空格有关!
AC代码:

class Solution {
public:
int lengthOfLastWord(string s) {
		int j = s.size() - 1;
		while (j >= 0 && s[j] == ' ') --j;
		int i = j;
		while (i >= 0 && s[i] != ' ') --i;
		return j - i;
	}
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值