算法拾遗三十一马拉车算法

news2025/7/14 9:26:50

算法拾遗三十一马拉车算法

      • 回文是什么
        • 回文暴力求法
      • Manacher算法
        • 回文直径和回文半径
        • 最右回文边界
        • 最右回文右边界的中心C位置
        • Manacher求解过程
        • Manacher 题

回文是什么

一个字符串正过来念和反过来念一样,总的来说就是有一个对称轴可能在字符上也可能在范围上面

回文暴力求法

在这里插入图片描述
首先以a为中心向两边扩散找回文,发现只有1是a的本身
以1位置为中心点扩大:
在这里插入图片描述
能扩大的长度为3,以2位置为中心向两边扩散回文最长为1,这样依次来扩,最终得到的回文序列为:
在这里插入图片描述
但是如上方法是搞不定偶数回文的情况的
在这里插入图片描述
在写代码的过程中可以处理一下:
有如下字符串:
在这里插入图片描述
那么我们可以处理为:
在这里插入图片描述
开头加一个#,以及每个字符的间隔都加一个#,然后以每一个为中心往左右两边扩,将得到的结果除以2。

将原始串转换为处理串,如果不用#,用一个在原始串中出现的字符,会不会干扰最终的处理结果?
不会的,因为往左右两边扩的时候是实际的字符和实际的字符相比,处理串新增的字符和处理串新增的字符比。

在这里插入图片描述
在这里插入图片描述
如上必然是个O(nxn的解)

如果使用Manacher算法则算法时间复杂度为O(N)

Manacher算法

回文直径和回文半径

在这里插入图片描述
回文直径是5,回文半径是3。
如1221,回文直径是4,回文半径是2

最右回文边界

用R表示,最开始R=-1
在这里插入图片描述
如果扩展的右边界在往右变大了,则更新R值
在这里插入图片描述
如上图R更新为0。

再到1位置往两边扩:
在这里插入图片描述
R来到2位置。
再从2位置往两边扩,来到4位置
在这里插入图片描述
R来到4位置。

再从3位置往两边扩,没有刷新右边界。
再从4位置往两边扩,没有刷新右边界

在这里插入图片描述
再从5位置向左右两边扩展,R来到10位置。
R始终记录着求当前遍历到回文串最右的位置

最右回文右边界的中心C位置

R只有更新,C就负责记录是以哪个位置为中心扩出来的R,C跟着R一起变
在这里插入图片描述
如上图从2位置扩展到4位置,R也来到4位置,则更新C为2位置。

Manacher求解过程

假设现在是在以i位置为中心向左右两边扩,
分为以下几种情况讨论:
第一种情况,i没有被R罩住(继续往左右两边扩)
第二种情况,i被R罩住 【存在优化:
在这里插入图片描述
C一定是i的左边的一个位置,我就能做出i关于C的对称点:
在这里插入图片描述
在这里插入图片描述

细分三种情况:
首先i在R外,暴力扩
在这里插入图片描述根据i‘扩出来的回文区域分,i’是小于i的,所以当初是求过i‘扩出来的大小的,而且当初求的答案一定被保存在R里面的。
第一种情况:i’扩出来的区域彻底在L-R内
在这里插入图片描述
i是当前来到的位置,L-R是C位置字符扩出来的回文串。
如下例子:
在这里插入图片描述
现在来到i位置,
在这里插入图片描述
这种情况扩的范围是跟i‘一样的,因为L-R已经是一个回文了,假设i位置和i’位置扩出同样长的区域,
在这里插入图片描述
甲和乙是关于C对称的,所以甲和乙是逆序的关系,由于甲是回文,所以甲的逆序也就是乙是回文。
第二种情况:i‘的回文区域已经跑到L-R的外面去了
在这里插入图片描述
在这里插入图片描述
i’的回文区域跑到L-R的外面去了,不用扩了,i位置能扩多远回文半径就是i-R。
证明:
在这里插入图片描述
以i‘为中心左L的对称点L’,以i为中心做R的对称点R‘
在这里插入图片描述
L-L’(叫甲)关于C的对称就是R‘到R(叫乙)这一段,甲和乙在L-R这个大回文里面,是关于C对称的,甲和乙是逆序的,甲又在i’为中心的回文里面,甲一定是回文,那么乙一定是回文。
在这里插入图片描述
在这里插入图片描述
第三种情况:i‘的回文区域和L在一起的
在这里插入图片描述
一定知道i的回文区域至少和i’一样,但是会不会更大不知道。
在这里插入图片描述

整个时间复杂度为O(N),因为R不回退的

如下这段代码,分为两个条件i在R内以及i在R外,
i在R外扩充区域最小为1,就是i自己。
在这里插入图片描述

i在R内扩充区域分三种情况:
在这里插入图片描述

public class Manacher {

	public static int manacher(String s) {
		if (s == null || s.length() == 0) {
			return 0;
		}
		// "12132" -> "#1#2#1#3#2#"
		char[] str = manacherString(s);
		// 回文半径的大小
		int[] pArr = new int[str.length];
		int C = -1;
		// 讲述中:R代表最右的扩成功的位置
		// coding:最右的扩成功位置的,再下一个位置
		int R = -1;
		int max = Integer.MIN_VALUE;
		for (int i = 0; i < str.length; i++) { // 0 1 2
			// R第一个违规的位置,i>= R
			// i位置扩出来的答案,i位置扩的区域,至少是多大。
			//i在R内,i‘和i到R的距离谁小取谁
			pArr[i] = R > i ? Math.min(pArr[2 * C - i], R - i) : 1;
			//i加上一个不用验证的区域,i减去一个不用验证的区域不越界
			while (i + pArr[i] < str.length && i - pArr[i] > -1) {
				//再往右的字符和再往左的字符一样,则扩展回文半径
				if (str[i + pArr[i]] == str[i - pArr[i]])
					pArr[i]++;
				else {
					break;
				}
			}
			//如果R更往右了,更新R更新C
			if (i + pArr[i] > R) {
				R = i + pArr[i];
				C = i;
			}
			max = Math.max(max, pArr[i]);
		}
		//半径减去一
		return max - 1;
	}

	public static char[] manacherString(String str) {
		char[] charArr = str.toCharArray();
		char[] res = new char[str.length() * 2 + 1];
		int index = 0;
		for (int i = 0; i != res.length; i++) {
			res[i] = (i & 1) == 0 ? '#' : charArr[index++];
		}
		return res;
	}

	// for test
	public static int right(String s) {
		if (s == null || s.length() == 0) {
			return 0;
		}
		char[] str = manacherString(s);
		int max = 0;
		for (int i = 0; i < str.length; i++) {
			int L = i - 1;
			int R = i + 1;
			while (L >= 0 && R < str.length && str[L] == str[R]) {
				L--;
				R++;
			}
			max = Math.max(max, R - L - 1);
		}
		return max / 2;
	}

	// for test
	public static String getRandomString(int possibilities, int size) {
		char[] ans = new char[(int) (Math.random() * size) + 1];
		for (int i = 0; i < ans.length; i++) {
			ans[i] = (char) ((int) (Math.random() * possibilities) + 'a');
		}
		return String.valueOf(ans);
	}

	public static void main(String[] args) {
		int possibilities = 5;
		int strSize = 20;
		int testTimes = 5000000;
		System.out.println("test begin");
		for (int i = 0; i < testTimes; i++) {
			String str = getRandomString(possibilities, strSize);
			if (manacher(str) != right(str)) {
				System.out.println("Oops!");
			}
		}
		System.out.println("test finish");
	}

}

在这里插入图片描述

2乘以C-i就是i‘,i’的回文半径长度和i-R的距离谁小,谁就是我至少不用验证的区域

Manacher 题

给定一个字符串,在字符串后面添加字符,至少添加几个字符让它整体都变成回文串。
【必须包含最后的字符的回文串最长是多长,然后将前面剩余的部分做逆序加到后面则解决问题】
在这里插入图片描述
求解方式:
在这里插入图片描述
在这里插入图片描述
当中心来到三角形位置的时候R包含最后一个字符,则停,已经找到了,将前面未包含的部分逆序到串后面则可以了。

package class28;

public class AddShortestEnd {

	public static String shortestEnd(String s) {
		if (s == null || s.length() == 0) {
			return null;
		}
		char[] str = manacherString(s);
		int[] pArr = new int[str.length];
		int C = -1;
		int R = -1;
		int maxContainsEnd = -1;
		for (int i = 0; i != str.length; i++) {
			pArr[i] = R > i ? Math.min(pArr[2 * C - i], R - i) : 1;
			while (i + pArr[i] < str.length && i - pArr[i] > -1) {
				if (str[i + pArr[i]] == str[i - pArr[i]])
					pArr[i]++;
				else {
					break;
				}
			}
			if (i + pArr[i] > R) {
				R = i + pArr[i];
				C = i;
			}
			if (R == str.length) {
				maxContainsEnd = pArr[i];
				break;
			}
		}
		char[] res = new char[s.length() - maxContainsEnd + 1];
		for (int i = 0; i < res.length; i++) {
			res[res.length - 1 - i] = str[i * 2 + 1];
		}
		return String.valueOf(res);
	}

	public static char[] manacherString(String str) {
		char[] charArr = str.toCharArray();
		char[] res = new char[str.length() * 2 + 1];
		int index = 0;
		for (int i = 0; i != res.length; i++) {
			res[i] = (i & 1) == 0 ? '#' : charArr[index++];
		}
		return res;
	}

	public static void main(String[] args) {
		String str1 = "abcd123321";
		System.out.println(shortestEnd(str1));
	}

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/590805.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

算法刷题总结 (十一) 二叉树

算法总结11 二叉树 一、二叉树的概念1.1、什么是二叉树&#xff1f;1.2、二叉树的常见类型1.2.1、无数值&#xff08;1&#xff09;、满二叉树&#xff08;2&#xff09;、完全二叉树 1.2.2、有数值&#xff08;3&#xff09;、二叉搜索树&#xff08;4&#xff09;、平衡二叉搜…

设置服务器ssh远程连接时超时关闭的时间

我们通过ssh远程连接服务器时&#xff0c;如果一段时间客户端没有使用&#xff0c;就会与服务器断开连接。这个断开的时间我们是可以自己的设置的。 以linux centos系统为例&#xff0c; 具体设置方法如下&#xff1a; 1、通过下面的命令编译sshd配置文件 vim /etc/ssh/sshd_…

SylixOS vutex

vutex 概念 SylixOS 引入了与 Linux futex 类似的用户快速锁 vutex&#xff08;vitual user mutex&#xff09;&#xff08;SylixOS 习惯称为“等待变量锁”&#xff09;&#xff1b;vutex 包括两个操作&#xff1a;pend 和 post&#xff0c;pend 操作用于等待期望值得到满足&…

DFS专题

题单地址&#xff1a;【237题】算法基础精选题单_ACM竞赛_ACM/CSP/ICPC/CCPC/比赛经验/题解/资讯_牛客竞赛OJ_牛客网 老子的全排列呢 dfs回溯 int n 8; int idx; int record[10]; bool vis[10];void dfs(int num) {if(numn){for(int i1;i<n;i) cout<<record[i]<…

【ONE·C++ || C++11(一)】

总言 主要介绍C11中的一些功能语法。 文章目录 总言0、思维导图1、基本情况简介2、统一的列表初始化2.1、{}的使用2.2、initializer_list2.2.1、基础介绍2.2.2、在各容器中实现说明 3、声明3.1、auto3.2、nullptr3.3、decltype 4、范围for5、智能指针6、STL中一些变化6.1、C11…

一、版本控制

1、什么是版本控制 1.1、版本控制的概念 版本控制&#xff08;Revision control&#xff09;是一种在开发的过程中用于管理我们对文件、目录或工程等内容的修改历史&#xff0c;方便查看更改历史记录&#xff0c;备份以便恢复以前的版本的软件工程技术。 1.2、版本控制的作用…

泛型方法、Function类的函数化编程与调用

0、引言 在项目开发的过程中&#xff0c;常常需要将一些高频复用的方法封装成工具类&#xff0c;例如最近学到的Redis缓存中&#xff0c;解决缓存穿透、解决缓存击穿的方法&#xff08;例如解决缓存穿透的问题的方法queryWithPassThrough&#xff09;&#xff0c;传入一个Long型…

谷粒商城:Oss endpoint can‘t be empty.问题

商品API &#xff0c;文件上传管理的时候 出现这个问题 解决两个方向 1.springBoot、alibabaCloud、springCloud、aliyunOSS 之间的版本问题&#xff0c;我的是下面的版本可以运行了。 // springBoot版本 2.7.7 <groupId>org.springframework.boot</groupId> &l…

中关村论坛 | 金融业从增量到存量博弈背后两大原因 更重要的是……

在数字经济浪潮下&#xff0c;中国金融业正在经历数字化转型的深刻变革。为研判金融科技行业发展趋势和前景&#xff0c;探索金融创新与监管安全的边界&#xff0c;“2023中关村论坛金融科技论坛”于5月29日召开。 中电金信常务副总经理冯明刚与中国银行软件中心副总经理康钧伟…

链表:虚拟头节点你会用吗?

大家好&#xff0c;我是三叔&#xff0c;很高兴这期又和大家见面了&#xff0c;一个奋斗在互联网的打工人。 前言&#xff1a;什么是链表 什么是链表&#xff0c;链表是一种通过指针串联在一起的线性结构&#xff0c;每一个节点由两部分组成&#xff0c;一个是数据域一个是指…

提高用户忠诚度的 4 种客户保留策略

什么是客户保留&#xff1f;简而言之&#xff0c;客户保留是指企业用来鼓励现有客户群重复购买和持续忠诚度的策略和战术。根据最近的研究&#xff0c;多达68%的客户在觉得公司不重视他们的业务时会转向竞争对手。 这就是为什么客户保留对各行各业的企业都如此重要的原因。与获…

《程序员面试金典(第6版)》面试题 16.25. LRU 缓存(自定义双向链表,list库函数,哈希映射)

题目描述 设计和构建一个“最近最少使用”缓存&#xff0c;该缓存会删除最近最少使用的项目。缓存应该从键映射到值(允许你插入和检索特定键对应的值)&#xff0c;并在初始化时指定最大容量。当缓存被填满时&#xff0c;它应该删除最近最少使用的项目。 题目传送门&#xff1a;…

消息队列内容

问题有哪些&#xff1f; &#xff08;1&#xff09;消息队列为什么会出现&#xff1f; &#xff08;2&#xff09;消息队列能用来干什么&#xff1f; &#xff08;3&#xff09;使用消息队列存在的问题&#xff1f; &#xff08;4&#xff09;如何解决重复消费的问题&#…

PyCharm安装使用教程

简介 PyCharm是一种PythonIDE&#xff08;Integrated Development Environment&#xff0c;集成开发环境&#xff09;&#xff0c;带有一整套可以帮助用户在使用Python语言开发时提高其效率的工具&#xff0c;比如调试、语法高亮、项目管理、代码跳转、智能提示、自动完成、单…

docker-安装redis集群

目录 1.服务器列表 2.安装docker 3.docker内网IP地址配置 4.docker安装redis集群 1.选择合适数据位置 2.循环生成redis配置目录 3.打开宿主机防火墙端口 4.循环生成redis容器 5.创建集群命令 6.命令行集群验证 1.服务器列表 服务器列表 nameip远程端口用户名/密码cen…

One2Multi Graph Autoencoder for Multi-view Graph Clustering

One2Multi Graph Autoencoder for Multi-view Graph Clustering | Proceedings of The Web Conference 2020 (acm.org) 目录 Abstract 1 Introduction 2 Model 2.1 Overview 2.2 One2Multi Graph Convolutional Autoencoder Informative graph convolutional encoder M…

Eclipse教程 Ⅸ

今天继续来学习Eclipse 快速修复、Eclipse 浏览菜单、Eclipse 查找以及Eclipse 悬浮提示的内容&#xff01;老规矩&#xff0c;废话不多说&#xff0c;开始吧。 Eclipse 快速修复 使用快速修复 在 Eclipse 编辑器中当你输入字母时&#xff0c;编辑器会对你输入的内容进行错误…

PostgreSQL FDW

一、FDW简单理解 FDW (foreign-data wrapper&#xff0c;外部数据包装器)&#xff0c;PostgreSQL FDW 是一种外部访问接口&#xff0c;它可以被用来访问存储在外部的数据&#xff0c;这些数据可以是外部的pg数据库&#xff0c;也可以oracle、mysql等数据库&#xff0c;甚至可以…

大气气溶胶期末复习笔记

大气气溶胶期末复习笔记 大气气溶胶 广义&#xff1a;指悬浮在大气中的各种固态和液态微粒与大气构成的混合体系 狭义&#xff1a;指大气中悬浮的各种固态粒子&#xff0c;简称气溶胶粒子 来源 直接注入 通过地表直接注入大气固体&#xff0c;液体物质的破碎过程中产生&…

筛质数—(埃氏筛欧拉筛)

埃氏筛&欧拉筛 埃氏筛欧拉筛 例题&#xff1a;AcWing 868. 筛质数 对欧拉筛的理解不是很深刻&#xff0c;写下自己的理解&#xff0c;加深一下理解&#xff0c;也方便后期忘记后再学习 埃氏筛 埃氏筛的主要思想是让质数x去筛掉x的所有合数&#xff0c;这个比较容易理解。…