LeetCode 184周赛

数组中的字符串匹配

用两层循环枚举每一对字符串,使用 contains 方法判断其是否被包含,找到一个包含关系就记录答案,结束里层循环。
需要注意的是拿到的一对字符串应该是不同的,不要把下标一样的进行判断。
代码:

class Solution {
    public List<String> stringMatching(String[] words) {
		List<String> ans = new ArrayList();
		int n = words.length;
		for(int i = 0;i < n;i++)
		{
			for(int j = 0;j < n;j++)
			{
				if(j == i || !words[j].contains(words[i]))
				{
					continue;
				}
				ans.add(words[i]);
				break;
			}
		}
		return ans;
    }
}

查询带键的排列

这个题目由于规模较小推荐直接模拟。由于其中即涉及到查找由涉及到移位,所以使用 LinkedList 、ArrayList 或者数组模拟复杂度都是O(mn),但是使用 LinkedList 会更快些,它省去了腾挪数据的时间。

如果本题的数据增强,还可以使用平衡树进行模拟,将复杂度将至 O(nlogm)。

这里就只放出O(nm)的程序了:

class Solution {
    public int[] processQueries(int[] queries, int m) {
		int n = queries.length;
		int[] ans = new int[n];
		
		LinkedList<Integer> list = new LinkedList();
		for(int i = 1;i <= m;i++)
		{
			list.add(i);
		}
		
		for(int i = 0,k;i < n;i++)
		{
			ans[i] = list.indexOf(queries[i]);
			list.remove(ans[i]);
			list.add(0, queries[i]);
		}
		return ans;
    }
}

HTML 实体解析器

这道题考验文本处理,只需要找到特定标志的字符串并且进行替换。

先给所有的替换做一个键值对 map
遍历整个字符串,是一般的字符就直接进行拼接。是 & 则开始连续读取直到 ; 。 匹配看是否有对应的翻译,没有就直接原样拼入。

代码:

class Solution {
   HashMap<String,String> map = new HashMap();
	
	
	public String entityParser(String text) {
		map.put("&quot;","\"");
		map.put("&apos;", "\'");
		map.put("&amp;", "&");
		map.put("&gt;", ">");
		map.put("&lt;", "<");
		map.put("&frasl;", "/");
		
		StringBuffer ans = new StringBuffer();
		
		int index = 0;
		char[] str = text.toCharArray();
		StringBuffer sign = new StringBuffer();
		while(index < str.length)
		{
			if(str[index] == '&')
			{
				sign.delete(0, sign.length());
				while(str[index] != ';')
				{
					sign.append(str[index++]);
				}
				sign.append(str[index++]);
				if(map.get(sign.toString()) == null)
				{
					ans.append(sign);
				}
				else
				{
					ans.append(map.get(sign.toString()));
				}
			}
			else
			{
				ans.append(str[index++]);
			}
		}
		return ans.toString();
    }
}

给 N x 3 网格图涂色的方案数

最后这题有点意思。

首先,为了表示三位,每位三种状态。可以使用二进制的两位当做一格。
每一格(两位二进制)用1(01),2(10),3(11)当做状态。

例如:"红绿蓝"记作 "011011"十进制为 1+2+8+16 = 27 。

这样,我们就可以使用一个数字表示一种组合了。

下面,我们需要解决这些位的运算问题,假设状态为 K,三格的取值分别为 a,b,c:

  • 获取状态 K = a | (b << 2) | (c << 4)
  • 取第k格的数字 num = (K >> k) & 3;
  • 判断数字 num 的奇偶,奇数为1,偶数为0:num & 1
  • 取与数字 num 奇偶性相反的值: num & 1 ^ 1;
  • 判断两数(以a b 为例)是否相等: a ^ b 为0 则等,否则不等

好了,有了上面的铺垫,这道题就可以开始解决了。

首先,不难发现,每行可以选择的状态是有限且确定的(先保证本行满足规则)。
那么我们就先把这些状态预处理出来,使用 nums 来记录共 cnt 个状态。

根据要求,相邻相同的状态是非法的。

for(int i = 1;i <= 3;i++)//第一位
	for(int j = 1 ;j <= 3;j++)//第二位
		for(int k = 1;k <= 3;k++)//第三位
		{
			if(i == j || j == k)//是否相邻相等
			{
				continue;
			}
			nums[cnt++] = (i << 4) | (j << 2) | k;
		}

(其实通过题目也可以得到,可行状态就是题目中给出的十二种)

接着,我们判断这些状态之间的可转移性,也就是两两是否可以做上下邻居,用 cnt*cnt 的 布尔类型表表示。

for(int i = 0;i < cnt;dp[0][i] = 1,i++)//顺带初始化dp数组
			for(int j = 0 ;j < cnt;j++)
			{
				if(!check(nums[i],nums[j]))//检查两状态是否可以相邻
				{
					next[i][j] = true;;
				}
			}

.
.
.
private boolean check(int a,int b)
	{
		int c = a ^ b;
		return (c & 3) == 0 || (c & 3 << 2) == 0 || (c & 3 << 4) == 0;//返回三位是否有相同的
	}

检查方式就是上文说的位运算。得到了这个转移矩阵,还需要初始化 dp 数组,将第一层 cnt 位全部置 1。
(这里补充说明一下 dp 数组的使用,由于每一层的值仅由上一层得到,所以采用滚动数组来进行转移。根据当前轮数的奇偶性计算出该使用哪一行。计算方式参见上文。博主的层数从 0 开始计所以初始化的是第0 行,答案为 n & 1 ^ 1 行的和)

最后一步就是进行递推转移了,有了刚刚的矩阵,对于每一种状态的,找出上一层所有可被拼接的状态的方案数,通过加法原理相加得到该层该状态的答案(记得取模哦。这里有点拗口,不过代码很好理解):

for(int i = 1;i < n;i++)//枚举层数(从第二层开始)
{
	for(int j = 0;j < cnt;j++)//当前层状态
	{
		dp[i & 1][j] = 0;	//初始化为0
		for(int k = 0;k < cnt;k++)//枚举上一层可能状态
		{
			if(next[j][k])//如果允许放置的话
			{
				dp[i & 1][j] = (dp[i & 1][j] + dp[i & 1 ^ 1][k] ) % mo;//统计答案
			}
		}
	}
}

最后答案就是最后一层的求和啦(还是不要忘记取模):

for(int i = 0;i < cnt;i++)
{
	ans = (ans + dp[n & 1 ^ 1][i]) % mo;
}
return ans;

完整代码:

class Solution {
    static int mo = 1000000007;
	int[] nums = new int[15];
	boolean[][] next = new boolean[15][15];
	
	private boolean check(int a,int b)
	{
		int c = a ^ b;
		return (c & 3) == 0 || (c & 3 << 2) == 0 || (c & 3 << 4) == 0;
	}
	
	public int numOfWays(int n) {
		int ans = 0;
		int cnt = 0;
		int[][] dp = new int[2][15];
		
		
		for(int i = 1;i <= 3;i++)
			for(int j = 1 ;j <= 3;j++)
				for(int k = 1;k <= 3;k++)
				{
					if(i == j || j == k)
					{
						continue;
					}
					nums[cnt++] = (i << 4) | (j << 2) | k;
				}
		
		for(int i = 0;i < cnt;dp[0][i] = 1,i++)
			for(int j = 0 ;j < cnt;j++)
			{
				if(!check(nums[i],nums[j]))
				{
					next[i][j] = true;;
				}
			}
		
		for(int i = 1;i < n;i++)
		{
			for(int j = 0;j < cnt;j++)
			{
				dp[i & 1][j] = 0;
				for(int k = 0;k < cnt;k++)
				{
					if(next[j][k])
					{
						dp[i & 1][j] = (dp[i & 1][j] + dp[i & 1 ^ 1][k] ) % mo;
					}
				}
			}
		}
		
		for(int i = 0;i < cnt;i++)
		{
			ans = (ans + dp[n & 1 ^ 1][i]) % mo;
		}
		return ans;
    }
}

猜你喜欢

转载自blog.csdn.net/wayne_lee_lwc/article/details/105467161