leetcode 字符串,子串,子序列,路径和相关问题
目录
leetcode 5 最长回文子串
- 2023.1.16 优化中心扩散的写法
var start,Len int
func longestPalindrome(s string) string {
//中心扩散记录 start和Len ,而且分为 两种情况,当前点左右扩散和当前和右边的点共同扩散
start = 0
Len = 0
for i:=0;i<len(s);i++{
getLenght(s,i,i+1)
getLenght(s,i,i)
}
return s[start:start+Len]
}
//计算长度
func getLenght(s string,l,r int){
for l>=0&&r<len(s)&&s[l]==s[r]{
l--
r++
}
if r-l-1>Len{
Len = r-l-1
start = l+1
}
}
- 暴力解法,每个字母向两边扩散计算,复杂度O(n^2)
func longestPalindrome(s string) string {
//两个for循环
res:=""
if len(s)==0{
return res
}
//没有则返回第一个
start,Len:=0,1
for i:=0;i<len(s);i++{
l,r:=i,i
for l>=0&&r<len(s)&&s[l]==s[r]{
l--
r++
}
if r-l+1-2>Len{
start = l+1
Len = r-l-1
}
l,r = i,i+1
for l>=0&&r<len(s)&&s[l]==s[r]{
l--
r++
}
if r-l+1-2>Len{
start = l+1
Len = r-l-1
}
}
tmp:=[]byte(s)
return string(tmp[start:start+Len])
}
- 优化解法(马拉车) 复杂度O(n)
马拉车参考教程
func longestPalindrome(s string) string {
//特殊判断
if len(s)<1{
return s
}
res:=s
start,Len:=0,0
size:=2*len(s)+1
//右侧最远和对应的中心
maxRight,center:=0,0
//获取处理之后的字符串
s=getstr(s)
//p记录对应的长度
p:=make([]int,size)
for i:=0;i<size;i++{
mirror:=2*center-i
if p[i]<maxRight-i{ //复用前面计算过的
p[i] = min(p[mirror],maxRight-i)
}
left,right:=i-(p[i]+1),i+(p[i]+1)
for left>=0&&right<size&&s[left]==s[right]{
p[i]++
left--
right++
}
//更新center和maxRight
if i+p[i]>maxRight{
maxRight = i+p[i]
center = i
}
//更新距离
if p[i]>Len{
Len = p[i]
start = (i-Len)/2
}
}
//从原来字符串截取
tmp:=[]byte(res)
return string(tmp[start:start+Len])
}
//增加间隔字符串
func getstr(str string)string{
res:=[]byte{'#'}
for i:=0;i<len(str);i++{
res = append(res,str[i])
res = append(res,'#')
}
return string(res)
}
func min(x,y int)int{
if x>y{
return y
}else{
return x
}
}
leetcode387 字符串中第一个唯一字符
计数数组记录26个字母的数量
func firstUniqChar(s string) int {
//记数数组
flag:=make([]int,26)
str:=[]rune(s)
for i:=0;i<len(str);i++{
flag[s[i]-'a']++
}
//找到第一个出现一次字符的位置
for i:=0;i<len(str);i++{
if flag[s[i]-'a']==1{
return i
}
}
return -1
}
8 实现atoi
注意可能出现的边界情况 -+123 纯空串“ ”
func myAtoi(s string) int {
max:=math.MaxInt32
min:=math.MinInt32
str:=[]rune(s)
//空返回0
if len(str)==0{
return 0
}
res:=0
flag:=true
i:=0
for ;i<len(str);i++{
if str[i]==' '{
continue
}else{
break
}
}
//前面全为空格
if i>=len(str) {
return 0
}
//判断正负号 可能出现-+情况
if str[i]=='-'{
flag = false
i++
}else if str[i]=='+'{
i++
}
for ;i<len(str);i++{
if str[i]>='0'&&str[i]<='9'{
if flag{
res = res*10+int(str[i]-'0')
if res>max{
res = max
break
}
}else{
res = res*10-int(str[i]-'0')
if res<min{
res = min
break
}
}
}else{
break
}
}
return res
}
451根据字符串出现频率进行排序
桶排序
c++ O(n) O(a)//a为出现次数最多的字符数
string frequencySort(string s) {
if(s.size()==0)return "";
//桶排序
unordered_map<char,int>hash;
int Maxlen = 0;
//放字符 保留最大值
for(int i=0;i<s.size();i++)
{
hash[s[i]]++;
Maxlen = max(Maxlen,hash[s[i]]);
}
//放ascill值 容量为出现最多次数+1而不是hash.size()
vector<vector<int>>buckets(Maxlen+1);
for(unordered_map<char,int>::iterator iter=hash.begin();iter!=hash.end();iter++)
{
buckets[iter->second].push_back(iter->first);//上面使用最大次数的原因,不用会溢出
}
//输出字符串
string res;
for(int i=Maxlen;i>=1;i--)
{
for(int j=0;j<buckets[i].size();j++)
{
//放置多次
for(int z = 0;z<i;z++)
{
res.push_back(buckets[i][j]);
}
}
}
return res;
}
1143. 最长公共子序列 (求具体序列)
- 简单dp,注意求具体序列时,是逆序,每行判断是否dp[i][j]==length并且比上面和左边dp值大
func longestCommonSubsequence(text1 string, text2 string) int {
//类似编辑距离 dp[i][j] 代表长度为i,j的最大编辑距离
if len(text1)==0||len(text2)==0{
return 0
}
m:=len(text1)
n:=len(text2)
dp:=make([][]int,m+1)
for i:=0;i<=m;i++{
dp[i] = make([]int,n+1)
}
for i:=1;i<=m;i++{
for j:=1;j<=n;j++{
if text1[i-1]==text2[j-1]{ //第i-1个 第j-1个最大子序列+1
dp[i][j] = dp[i-1][j-1]+1
}else{
dp[i][j] = max(dp[i-1][j],dp[i][j-1])
}
}
}
//计算具体序列
length:=dp[m][n]
res:=[]byte{}
for i:=m;i>0;i--{
for j:=n;j>0;j--{
if dp[i][j]==length&&dp[i][j]>dp[i-1][j]&&dp[i][j]>dp[i][j-1]{
res = append([]byte{text1[i-1]},res...) //先出现的放在后面
length--
break
}
}
}
fmt.Println(string(res))
return dp[m][n]
}
- 2022.5.23
func longestCommonSubsequence(text1 string, text2 string) int {
//类似编辑距离 dp[i][j] 代表长度为i,j的最大编辑距离
if len(text1)==0||len(text2)==0{
return 0
}
m:=len(text1)
n:=len(text2)
dp:=make([][]int,m+1)
for i:=0;i<=m;i++{
dp[i] = make([]int,n+1)
}
for i:=1;i<=m;i++{
for j:=1;j<=n;j++{
if text1[i-1]==text2[j-1]{ //第i-1个 第j-1个最大子序列+1
dp[i][j] = dp[i-1][j-1]+1
}else{
dp[i][j] = max(dp[i-1][j],dp[i][j-1])
}
}
}
//计算具体序列
length:=dp[m][n]
res:=[]byte{}
for i:=m;i>0;i--{
for j:=n;j>0;j--{
if dp[i][j]==length&&dp[i][j]>dp[i-1][j]&&dp[i][j]>dp[i][j-1]{
res = append([]byte{text1[i-1]},res...) //先出现的放在后面
length--
break
}
}
}
fmt.Println(string(res))
return dp[m][n]
}
64 最小路径和(求具体路径)
func minPathSum(grid [][]int) int {
//简单dp 扩展为求具体路径
m:=len(grid)
n:=len(grid[0])
dp:=make([][]int,m)
for i:=0;i<m;i++{
dp[i] = make([]int,n)
}
dp[0][0] = grid[0][0]
for i:=1;i<m;i++{ //初始化边缘
dp[i][0] = dp[i-1][0]+grid[i][0]
}
for i:=1;i<n;i++{
dp[0][i] = dp[0][i-1]+grid[0][i]
}
for i:=1;i<m;i++{
for j:=1;j<n;j++{
dp[i][j] = min(dp[i-1][j],dp[i][j-1])+grid[i][j]
}
}
//求具体路径,倒序推理
list:=make([]int,0)
i,j:=m-1,n-1
list = append(list,grid[i][j])
sum:=dp[m-1][n-1]
for i>0||j>0{
sum-=grid[i][j]
if j-1>=0&&dp[i][j-1]==sum{ //一直到0的位置
list = append(list,grid[i][j-1]) //list = append(list,[]int{i,j-1}) 更换为坐标
j--
}else{
list = append(list,grid[i-1][j])
i--
}
}
fmt.Print(list) //最后得到的是逆序的路径,reverse后即可得到正序
return dp[m-1][n-1]
}