`
clxy
  • 浏览: 79938 次
社区版块
存档分类
最新评论

字符串消除

 
阅读更多
  这篇一早写好,原打算等竞赛结束再贴。可昨天发现网上已到处都有讨论及解答,就不再等了。

  前些日子玩的一道题,虽然解题失败,但还是觉得值得记上一笔。题是这样的

给定一个字符串,仅由a,b,c 3种小写字母组成。当出现连续两个不同的字母时,你可以用另外一个字母替换它,如
 - 有ab或ba连续出现,你把它们替换为字母c;
 - 有ac或ca连续出现时,你可以把它们替换为字母b;
 - 有bc或cb 连续出现时,你可以把它们替换为字母a。
你可以不断反复按照这个规则进行替换,你的目标是使得最终结果所得到的字符串尽可能短,求最终结果的最短长度。 

输入:字符串。长度不超过200,仅由abc三种小写字母组成。 
输出:按照上述规则不断消除替换,所得到的字符串最短的长度。 

例如:
	输入cab,输出2。因为我们可以把它变为bb或者变为cc。 
	输入bcab,输出1。尽管我们可以把它变为aab -> ac -> b,也可以把它变为bbb,但因为前者长度更短,所以输出1。


  我对纯粹的数学公式推导还是很怕的。年纪越大,越偏文了?但这道题比起公式推导,推理的味道更浓些。

  失败的原因是超时。似乎应该先读题,分析及做好准备后,再去答题。我直接点了进去,在倒计时的恫吓下,两个小时里啥都没干成...

  挑战失败后,反倒静下心来。最先找到的规律就是重复的字母可以化简,比如三个a和一个a是等价的,4个b和2个b也是等价的。于是直接开写实现这个化简逻辑的代码。M大人也被我拖下水,途中参战。不愧是M大人,思路很直接:“每次消除时都能保证最短即可!”先找感觉再找理论支撑是M大人的风格。结果证明这个思路也是对的!一发命中也是M大人的风格!

  我这时却陷入了困境——即便整理好重复的字符,依然a啊b啊c的团团混杂,看不出个所以然。在我打算放弃,M大人的一句话点亮了我的解题道路——虽然她自己不大在意——“c其实等价于ab!”。是的,如果c等价于ab,那么把c用ab(或ba)替换掉后,字符串就只剩下ab了,就又可以整理重复的字符了!哇哈哈!到这里才发现,原来我的思路就只是“整理”!”

  这回学乖了,不写代码了,先观察分析——总是忘记这条多年来学到宝贵经验。我发现如果相同的奇数个字符无论多少个都会产生同样结果,偶数也同样。这样可以进一步化简,所有重复的奇数字符都算作一个,所有偶数的都算作两个。字符串最终化简成axbxax... x可能是1或2。到这里规律很容易找到:

c替换成ab;统计ab的个数;当a和b的个数都是偶数时,结果是两个;否则是一个。


  M大人最终的逻辑则复杂些:

遍历字符串;尝试消除字符;不能消除则继续;如能消除,用再前一位和再后一位字符评价是否能保证最短(=即不同),能则消,否则继续;一旦有消除发生,重新遍历消除后的新字符串;遍历结束时检查是否存在可以消除但却没通过评价的情况,如果有则消除,重新遍历。


  上面的逻辑再加上,如果该字符串全部字符相同——比如都是a——这样的特殊情况就全活了!

  这里的论证不是那么严谨,但是结果我觉得是相当的...理想。

代码如下:
public class EraseChar {

	private static Character[] chars = { 'a', 'b', 'c' };

	public static void main(String args[]) {

		Set<String> strings = new HashSet<>();

		// start === 测试数据。
		for (int i = 0; i < 100000; i++) {
			fillRandom(strings);
		}
		strings.add("a");
		strings.add("b");
		strings.add("c");
		strings.add("ab");
		strings.add("abc");
		strings.add("aa");
		strings.add("aaa");
		strings.add("aaaa");
		strings.add("cacccc");
		// end === 测试数据。

		for (String s : strings) {
			int min1 = gg(s);
			int min2 = mm(s);
			System.out.println("min1=" + min1 + ", min2=" + min2 + " {" + s + "}");
			if (min1 != min2) {
				throw new RuntimeException("Noooooooo!");
			}
		}
	}

	/**
	 * c替换成ab;统计ab的个数;当a和b的个数都是偶数时,结果是两个;否则是一个。
	 * @param s
	 * @return
	 */
	public static int gg(String s) {

		char[] chars = s.toCharArray();

		int length = chars.length;
		if (isAllSame(chars)) {
			return length;
		}

		int countA = 0;
		int countB = 0;

		for (char c : chars) {
			switch (c) {
			case 'c':
				countA++;
				countB++;
				break;
			case 'a':
				countA++;
				break;
			case 'b':
				countB++;
				break;
			}
		}

		if (countA % 2 == 0 && countB % 2 == 0) {
			return 2;
		}

		return 1;
	}

	public static int mm(String s) {

		char[] chars = s.toCharArray();

		int length = chars.length;
		if (isAllSame(chars)) {
			return length;
		}

		LinkedList<Character> list = new LinkedList<>();
		for (char c : chars) {
			list.add(c);
		}

		return getMimi(list);
	}

	/**
	 * <ol>
	 * <li>尝试消除字符;
	 * <li>如不能消除则继续;
	 * <li>如能消除,用再前一位和再后一位字符评价是否能保证最短(=即不同),能则消,否则继续;
	 * <li>一旦有消除发生,重新遍历消除后的新字符串;
	 * <li>遍历结束时检查是否存在可以消除但却没通过评价的情况,如果有则消除,重新遍历。
	 * </ol>
	 * @param list
	 * @return
	 */
	public static Integer getMimi(List<Character> list) {

		int size = list.size();
		if (size == 1) {
			return 1;
		}

		if (size == 2) {
			return list.get(0).equals(list.get(1)) ? 2 : 1;
		}

		Integer position = null;
		for (int i = 0, l = list.size() - 1; i < l; i++) {

			Character first = list.get(i);
			Character second = getElement(list, i + 1);
			Character test = test(first, second);
			if (test == null) {
				continue;
			}

			Set<Character> comps = new HashSet<>();
			Character prev = getElement(list, i - 1);
			if (prev != null) {
				comps.add(prev);
			}
			Character next = getElement(list, i + 2);
			if (next != null) {
				comps.add(next);
			}
			comps.remove(test);
			if (comps.isEmpty()) {
				position = (position == null) ? i : position;
				continue;
			}

			replace(list, i, test);
			return getMimi(list);
		}

		if (position == null) {
			return list.size();
		}

		Character first = list.get(position);
		Character second = getElement(list, position + 1);
		Character test = test(first, second);
		replace(list, position, test);
		return getMimi(list);
	}

	private static boolean isAllSame(char[] chars) {

		Character temp = null;
		for (char c : chars) {
			if (temp == null) {
				temp = c;
				continue;
			}

			if (!temp.equals(c)) {
				return false;
			}
		}
		return true;
	}

	private static Character test(char c1, char c2) {
		return (c1 == c2) ? null : minus(c1, c2);
	}

	private static void replace(List<Character> list, int i, char c) {
		list.remove(i);
		list.remove(i);
		list.add(c);
	}

	private static Character getElement(List<Character> list, int i) {
		return (i >= list.size() || i < 0) ? null : list.get(i);
	}

	private static char minus(char c1, char c2) {
		for (char c : chars) {
			if (c != c1 && c != c2) {
				return c;
			}
		}
		throw new RuntimeException("wrong char!");
	}

	private static void fillRandom(Set<String> strings) {

		Random r = new Random();
		int size = r.nextInt(200);
		StringBuilder sb = new StringBuilder(size);
		for (int i = 0; i < size; i++) {
			sb.append(chars[r.nextInt(3)]);
		}
		String s = sb.toString();
		if (strings.contains(s)) {
			fillRandom(strings);
		}
		strings.add(s);
	}

}

1
3
分享到:
评论
1 楼 GodisaAVman 2013-09-16  
非常有意思,闲着我也来搞一下

相关推荐

    字符串的比较

    1. **大小写标准化:** 首先通过调用`Character.toLowerCase(Character.toUpperCase(character))`方法来标准化两个字符串中的每个字符,这样可以消除大小写差异。 2. **按字典顺序比较:** 接下来,按字典顺序比较...

    deline一个ES6字符串标记能够删除多行字符串中多余的换行

    deline库利用ES6的字符串处理功能,提供了一个简洁的方法来消除这些冗余的换行。它的核心思想是解析字符串,识别并合并连续的换行符,只保留必要的行间隔。这种方法可以确保文本在展示时保持适当的格式,同时避免了...

    matlab简单代码-《如何在 MATLAB 中删除字符串中的空格?》实例教程下载

    在 MATLAB 中,删除字符串中的空格是一个常见的任务,特别是在处理数据清理或文本分析时。MATLAB 提供了几种方法来实现这一目标,包括 `isspace()`、`find()`、`strrep()`、`regexprep()` 以及 `deblank()` 函数。...

    计算机语言python标准库

    python 标准库 字符串 ... 返回原字符串消除大小写的副本。 消除大小写的字符串可用于忽略大小写的匹配。 str.count`(*sub*[, *start*[, *end*]]) 返回子字符串 *sub* 在 [*start*,

    字符串压缩

    字符串压缩的主要目的是通过消除数据中的冗余来降低存储需求,这在处理大量文本数据时尤其有用。在编程领域,有许多不同的压缩算法,如霍夫曼编码、LZ77、LZ78、DEFLATE(广泛应用于ZIP和GZIP格式)等。 描述中提到...

    c#实例_处理字符串的源代码

    在C#编程语言中,处理字符串是一项常见的任务,特别是在处理大量文本数据时。"c#实例_处理字符串的源代码"这个项目显然专注于提供解决此类问题的解决方案。在这个实例中,我们将探讨如何去除文本文件中重复的行以及...

    Rust的 sift4 字符串距离算法_rust_代码_下载

    Levenshtein距离是最常见的字符串距离度量,它计算了将一个字符串转换为另一个字符串所需的最少单字符编辑(插入、删除或替换)次数。然而,对于大规模数据或实时应用,Levenshtein的计算成本可能过高。 sift4算法...

    C语言 设计字符串比较的函数和销售员业绩管理程序

    在本课程设计中,我们将探讨如何使用C语言来实现字符串比较的函数以及一个销售员业绩管理程序。首先,我们来看字符串比较的实现。 1. 字符串比较: - **最大字符查找**:我们需要创建一个函数,接收两个长度为10的...

    最全的Jython学习资料:来自官网(二) 字符串模块

    4. dedent函数:此函数用于去除字符串左侧不期望的空白(例如,用于消除多行字符串中的缩进空白)。dedent函数作用于文本的每一行,移除最左侧的共同空白。这对于编写文档字符串时非常有用,例如,希望在显示时左...

    C# 去掉特定字符(使用ASC码)

    在C#编程中,有时我们需要处理字符串,去除其中的特定字符。这可能是因为这些字符对程序的处理或者数据的解析产生了不利影响。本方法提供了一种解决方案,即通过ASCII码来识别并移除字符串中的特定字符。下面我们将...

    《字符串模式匹配KMP算法》教学课例设计[归纳].pdf

    《字符串模式匹配KMP算法》教学课例设计 在这篇教学设计中,我们旨在帮助学生掌握KMP字符串模式匹配算法的基本概念和应用。通过本课例设计,学生将了解KMP算法的应用普遍性、实现机制和时间复杂度,并掌握计算next...

    利用opencv把视频转换成字符串视频

    在这个场景中,我们将探讨如何利用OpenCV将视频转换为“字符串视频”。 “字符串视频”是一种创新的数据表示方式,它通过将视频帧转化为字符或文本形式,实现视频信息的另类存储和传输。这种技术通常基于图像转字符...

    UTF8字符串转换工具绿色版

    UTF8字符串转换工具是一款便捷实用的软件,专为处理各种编码格式之间的字符串转换问题而设计。在信息技术领域,字符编码是至关重要的,因为它决定了计算机如何存储、处理和显示文本。UTF8作为最广泛使用的Unicode...

    消除字符串重复字母(栈实现)

    在给定的编程题目"消除字符串重复字母(栈实现)"中,主要涉及了字符串处理、字符数组操作以及栈数据结构的应用。以下是针对这个题目详细的知识点解析: 1. **字符串处理**: - 字符串是编程语言中常用的数据类型...

    Oracle 多行记录合并_连接_聚合字符串的几种方法_oracle_脚本之家1

    Oracle数据库在处理多行记录合并、连接和聚合字符串时,有多种方法,下面将详细介绍其中的几种常见技术。 1. 被集合字段范围小且固定型 这种方法适用于字段值有限且已知的情况。通过使用`DECODE`函数,我们可以为每...

    处理字符串除字符串头部和尾部所包含的空格、制表符、换页符、消除开头的制表符、消除结尾的制表符、去空格方法、读取文本后又转成文本

    1.去除字符串头部和尾部所包含的空格、制表符、换页符(全角和半角), 2.消除开头的制表符 3.消除结尾的制表符 4.去空格方法 5.读取文本后又转成文本

    RE_字符串1

    我们把原字符串反转,然后用KMP算法进行匹配,找到的将是原字符串的最长后缀,这个后缀同时是原字符串的前缀,也就是回文串。 【正则表达式匹配】 正则表达式匹配问题,例如LeetCode的Q10,涉及到使用动态规划来...

    VB.NET《MD5加密字符串(Excel加密字符串+字符串验证).zip

    这段代码首先将输入的字符串转为ASCII字节数组,然后通过MD5CryptoServiceProvider计算哈希值,最后将哈希值转为Base64编码的字符串。注意,实际应用中,通常会将Base64编码替换为直接显示16进制字符串,这可以通过...

Global site tag (gtag.js) - Google Analytics