回文字符算法(最长的公共子序列和公共子串)


今天的主要的目标是:
1, 学习一个算法:最长的回文的算法(最长的公共子序列和最长的公共子串)
2, 搞明白一个并发编程的类: 原子类
3, 外加一个知识点:原子类的用法

一个算法:最长的回文字符

/**
	 * 第一种方法:采用最长的子串
	 * 把要求的字符串s,颠倒一下变为s`,然后求取两个字符串的最长公共子串,LCS
	 * 这个是把未知的问题转化为已知的问题
	 * */

	public String longestPalindrome1(String s) {
		//构建辅助的s`
		String ss = changeOver(s);
		String lcs = longestCommomSubstring(s, ss);
		return lcs;
	}

  /**
	 * 求取两个字符串的最长的公共子序列<br>
	 * 采用的是动态规划的算法,两个字符串s1,s2 怎么算出最长的公共子序列的?状态的转移的方程是什么样子的?<br>
	 * 假设 km 是字符转ai 和 bj 的最长的公共的子序列。k[0,1,2,......m] = lcs(a[0,1,2,3,......i],b[0,1,2,3,......j];<br>
	 * 那么如果字符a[i] == b[j] 那么km = a[i] ,k[0,1,2,......m-1] = lcs(a[0,1,2,3,......i-1],b[0,1,2,3,......j-1];<br>
	 * 那么如果字符a[i] != b[j] 并且km = a[i] ,k[0,1,2,......m-1] = lcs(a[0,1,2,3,......i-1],b[0,1,2,3,......j];<br>
	 * 那么如果字符a[i] != b[j] 并且km = b[j] ,k[0,1,2,......m-1] = lcs(a[0,1,2,3,......i],b[0,1,2,3,......j-1];<br>
	 * 最后的总结是:k[0,1,2,......m-1] = max{lcs(a[0,1,2,3,......i],b[0,1,2,3,......j-1],lcs(a[0,1,2,3,......i-1],b[0,1,2,3,......j]};<br>
	 *
	 * 然后我们就可以得到:需要一个中间变量来表示a[i]是否等于b[j],这个时候就能自然而然的想起二维数组来进行表示k[i][j],如果是使用k[i][j]等于1来表示<br>
	 * a[i] == b[j],那么我们根据k[i][j] 就能够找到最长的公共子序列,这个时候的状态转移方程,也会有相应的变化<br>
	 *
	 * 如果k[i][j] = 1 ,那么:k[0,1,2,......m] = 1+lcs(a[0,1,2,3,......i],b[0,1,2,3,......j];<br>
	 * 如果k[i][j] = 0 ,那么:k[0,1,2,......m] =  max{lcs(a[0,1,2,3,......i],b[0,1,2,3,......j-1],lcs(a[0,1,2,3,......i-1],b[0,1,2,3,......j]};<br>
	 *
	 * @param first
	 * @param second
	 * @return
	 */

//	最长公共子串(Longest Common Substring)指的是两个字符串中的最长公共子串,要求子串一定连续。
//	最长公共子序列(Longest Common Subsequence)指的是两个字符串中的最长公共子序列,不要求子序列连续。
	public static String longestCommomSubsequence(String first, String second) {
		if(first == null || first.length() <= 0 || second == null || second.length() <= 0){
			return null;
		}
		char[] fir = first.toCharArray();
		char[] sec = second.toCharArray();
		int f = fir.length;
		int s = sec.length;
		// 首先是初始化
		int[][] k = new int[f][s];
		for (int i = 0; i < f; i++) {
			for (int j = 0; j < s; j++) {
				if(fir[i] == sec[j]){
					if(i == 0||j== 0){
						k[i][j]=1;
					}else{
						k[i][j]=k[i-1][j-1]+1;
					}

				}else{
					if(i == 0||j== 0){
						k[i][j]=0;
					}else{
						k[i][j]=Math.max(k[i-1][j],k[i][j-1]);
					}
				}
			}
		}

		// 输出数组结果进行观察  
        for (int i = 0; i < f ; i++) {  
            for (int j = 0; j < s ; j++) {  
                System.out.print(k[i][j]+",");  
            }  
            System.out.println("");  
        }  
        int ff = fir.length-1;
		int ss = sec.length-1;
        int maxlength = k[ff][ss];
        StringBuffer sb=new StringBuffer();
        while(maxlength > 0 && ss >= 0 && ff>=0){
        	System.out.println(fir[ff]+","+sec[ss]);
        	if(fir[ff] == sec[ss]){
        		System.out.println(k[ff][ss]);
        		sb.append(fir[ff]);
				ff = ff-1;
				ss = ss-1;
				maxlength--;
        	}else{
        		if (k[ff][ss-1]>k[ff-1][ss]) {
        			ss = ss-1;
				}else {
					ff =ff-1;
				}
        	}
        }
      return sb.reverse().toString();
	}

  /**
	 * 得到最长的公共子串
	 * */
	public static String longestCommomSubstring(String first, String second) {
		if(first == null || first.length() <= 0 || second == null || second.length() <= 0){
			return null;
		}
		char[] fir = first.toCharArray();
		char[] sec = second.toCharArray();
		int ff = fir.length;
		int ss = sec.length;
		int[][]k = new int[ff][ss];
		int max = 0;int imax = 0,jmax=0;
		for (int i = 0; i < ff; i++) {
			for(int j = 0;j<ss;j++){
				if(fir[i] == sec[j]){
					if(i == 0||j==0){
						k[i][j]=1;
					}else{
						if(k[i-1][j-1] > 0){
							k[i][j]=k[i-1][j-1]+1;
						}else{
							k[i][j]=1;
						}
					}
				}else{
					k[i][j]=0;
				}

				if(k[i][j] > max){
					imax=i;
					jmax =j;
					max= k[i][j];
				}
			}
		}

		StringBuilder res  = new StringBuilder();
        for (int i = imax,j = jmax; i >= 0 && j >= 0 && max>0 ; i--,j--) {
        	res.append(fir[i]);
        	max--;
        }  
		return res.reverse().toString();
	}

	public static void main(String[] args){
		 String a = "csdn.ertieiteitoebteoteteutt";  
	        String b = "csdn.bt";  
	        System.out.println(longestCommomSubsequence(a, b));
	}

一个具体的类:java.util.concurrent.atomic.AtomicInteger
声明的变量:private volatile int value;

   /** 1.8的jdk
     * Atomically increments by one the current value.
     *
     * @return the updated value
     */
    public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }

    // 1.5的
    public final int incrementAndGet() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return next;
        }
    }

java.util.concurrent.atomic.AtomicInteger 采用的是:java中CAS的实现

CAS就是Compare and Swap的意思,比较并操作。很多的cpu直接支持CAS指令。CAS是项乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。

JDK1.5中引入了底层的支持,在int、long和对象的引用等类型上都公开了CAS的操作,并且JVM把它们编译为底层硬件提供的最有效的方法,在运行CAS的平台上,运行时把它们编译为相应的机器指令。在Java.util.concurrent.atomic包下面的所有的原子变量类型中,比如AtomicInteger,都使用了这些底层的JVM支持为数字类型的引用类型提供一种高效的CAS操作。

在CAS操作中,会出现ABA问题。就是如果V的值先由A变成B,再由B变成A,那么仍然认为是发生了变化,并需要重新执行算法中的步骤。有简单的解决方案:不是更新某个引用的值,而是更新两个值,包括一个引用和一个版本号,即使这个值由A变为B,然后为变为A,版本号也是不同的。AtomicStampedReference和AtomicMarkableReference支持在两个变量上执行原子的条件更新。AtomicStampedReference更新一个“对象-引用”二元组,通过在引用上加上“版本号”,从而避免ABA问题,AtomicMarkableReference将更新一个“对象引用-布尔值”的二元组。



上一篇  梳理一些的基础性的知识 下一篇   一周学习总结