数组中的字符串匹配
用两层循环枚举每一对字符串,使用 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(""","\"");
map.put("'", "\'");
map.put("&", "&");
map.put(">", ">");
map.put("<", "<");
map.put("⁄", "/");
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;
}
}