文章目录
1.Algorithm
1.1 最长公共前缀
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 “”。
示例 1:
输入: [“flower”,“flow”,“flight”]
输出: “fl”
示例 2:
输入: [“dog”,“racecar”,“car”]
输出: “”
解释: 输入不存在公共前缀。
说明:
所有输入只包含小写字母 a-z 。
1.1.1 解法一 依次比较每个位置上的元素
从第一个字符串的第一个元素开始遍历,
如果其它字符串在相同位置也是该元素则继续,否则返回
注意:
输入可能是空字符串[]
输入是[a,a],[a]
要判断当前位置小于字符串长度
方法String.substring(beginIndex,endIndex)
输出不包括endIndex位置
class Solution {
public String longestCommonPrefix(String[] strs) {
if (strs.length == 0) return "";
int i = 0;
char temp;
for (i = 0; i < strs[0].length(); i++) {
temp = strs[0].charAt(i);
for (int j = 1; j < strs.length; j++) {
if (strs[j].length() == i || !(temp == strs[j].charAt(i))) {
return strs[0].substring(0, i);
}
}
}
return strs[0].substring(0, i);
}
}
1.1.2 解法二 先将第一个字符串默认为最长
公共前缀==
第一个字符串
公共前缀和第二个字符串==》
公共前缀
公共前缀和第三个字符串==》
公共前缀
如果公共前缀为""
则退出
否则使用String.indexOf()
如果结果不等于0,那么去掉公共前缀的末尾再比较
class Solution {
public String longestCommonPrefix(String[] strs) {
if (strs.length == 0) return "";
String temp = strs[0];
for (String str : strs) {
while (temp.length() != 0) {
if (str.indexOf(temp) != 0) {
temp = temp.substring(0, temp.length() - 1);
} else {
break;
}
}
}
return temp;
}
}
时间复杂度:O(S) S是字符数组长度
控件复杂度:O(1)
1.1.3 分治法
- 将数组分为左右两部分
- 最长公共前缀=左最长 与 右最长比较
- 左最长 重复1 2
- 右最长 重复1 2
- 当数组元素只有一个时,最长为本身,返回当前为最长公共前缀
- 获取左最长与右最长的比较后的公共前缀
class Solution {
public String longestCommonPrefix(String[] strs) {
if(strs.length == 0) return;
return longestCommonPrefix(strs,1,strs.length);
}
private String longestCommonPrefix(String[] ,int left ,right right){
//临界点
if(left == right)
return(strs[left]);
String leftResult = longestCommonPrefix(strs, left, (left + right) / 2);
String rightResult = longestCommonPrefix(strs, (left + right) / 2, right);
commonPrefix(leftResult, rightResult);
}
//比较两个字符串的公共前缀
//方案1:依次比较相同位置的元素
commonPrefix(String leftResult,String rightResult){
for(int i = 0; i < leftResult.length(); i++){
if(rightResult.length() <= i || rightResult.charAt(i)!= leftResult.charAt(i) )
return leftResult.substring(0,i);
}
}
//比较两个字符串的公共前缀
//方案2:将一个字符串作为prefix,在比较中缩小prefix
commonPrefix2(String leftResult,String rightResult){
while(rightResult.indexOf(leftResult) != 0){
leftResult = leftResult.substring(0,leftResult.length()-1);
if(leftResult.isEmpty())
return "";
}
return leftResult;
}
}
1.1.4 二分查找
- 首先获取最小的元素长度minLength
- 将第一个元素从0到minLength作为profix;因为最长profix符合和任意一个匹配都是最长
- 与其它元素循环比较时,不去从profix的第一个下标开始比较,而是使用二分查找来比较
- 先比较从0到middle是否相等
- 相等,那么再比较从0 到 middle=(left(middle+1) + right )/2
- 不相等,那么再比较从0 到 middle=(left + right(middle-1) )/2
- 直到right < left
- 此时 profix = strs.substring(0,middle)
- 注意:middle为数组的长度,比下标大1,起始left=1;right=strs.length;
典型二分查找:判断arr[middle]==val;判断是否相等
该二分查找:判断 strs[i].startWith(profix.substring(0,middle));判断是否公共前缀;
归纳:
- 对数据源
arr 和 profix
进行二分 - 二分后的值
arr[middle] 和 profix.substring(0,middle)
- 判断是否满足条件
==val 和 是所有元素的公共前缀
class Solution {
public String longestCommonPrefix(String[] strs) {
if (strs.length == 0) return "";
int height = Integer.MAX_VALUE;
for (String str : strs) {
height = Math.min(height, str.length());
}
int low = 1;
while (low <= height) {
int middle = (low + height) / 2;
String profix = strs[0].substring(0, middle);
if (isCommonPrefix(strs, profix)) {
low = middle + 1;
} else {
height = middle - 1;
}
}
//长度是几?
return strs[0].substring(0, (low + height) / 2);
}
//比较两个字符串的公共前缀
//方案3:二分查找
private boolean isCommonPrefix(String[] strs, String profix) {
for (int i = 1; i < strs.length; i++) {
if (!strs[i].startsWith(profix))
return false;
}
return true;
}
}
1.1.5 前缀树
参考:前缀树实现
需要对最简单的前缀树添加size
属性
如果size长度为1则说明是公共前缀
class Solution {
public String longestCommonPrefix(String[] strs) {
if (strs.length == 0) return "";
Trie trie = new Trie();
for (String str : strs) {
trie.insert(str);
}
return trie.getProfixStr(strs[0]);
}
/**
* 节点类
*/
class TrieNode {
private TrieNode[] trieNodes;
//共有26个英文字母
private final int length = 26;
//是否为结尾节点
private boolean isEnd;
//非空节点长度
private int size = 0;
public TrieNode() {
this.trieNodes = new TrieNode[length];
}
public void put(char ch, TrieNode trieNode) {
this.trieNodes[ch - 'a'] = trieNode;
size++;
}
public TrieNode get(char ch) {
return this.trieNodes[ch - 'a'];
}
public boolean containsKey(char ch) {
return this.trieNodes[ch - 'a'] != null;
}
public boolean isEnd() {
return this.isEnd;
}
public void setEnd() {
this.isEnd = true;
}
public int getSize() {
return this.size;
}
}
//前缀树结构
class Trie {
//当前节点
private TrieNode root;
/**
* Initialize your data structure here.
*/
public Trie() {
this.root = new TrieNode();
}
/**
* Inserts a word into the trie.
*/
public void insert(String word) {
//从根节点开始
TrieNode trieNode = this.root;
for (int i = 0; i < word.length(); i++) {
char currentChar = word.charAt(i);
if (!trieNode.containsKey(currentChar))
trieNode.put(currentChar, new TrieNode());
//下一个节点,处理下一个元素
trieNode = trieNode.get(currentChar);
}
trieNode.setEnd();
}
//获取最长公共前缀
public String getProfixStr(String str) {
TrieNode trieNode = this.root;
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < str.length(); i++) {
char currentChar = str.charAt(i);
if (trieNode.getSize() != 1 || trieNode.isEnd())
return stringBuilder.toString();
else {
stringBuilder.append(currentChar);
trieNode = trieNode.get(currentChar);
}
}
return stringBuilder.toString();
}
}
}
2.Review
3.Tip
3.1 .net 使用 StateServer 保存session
场景:
应用程序部署在两台服务器,通过域名映射到两台服务器的ip A 和 B上面
当用户登录以后,访问的是服务器ip A,过段时间再次访问,域名解析可能会将ip映射到服务器ip B上
此时会发现session丢失
原因:
原始配置,两台服务器的session保存在各自服务器的 aspnet_state服务中
<sessionState
cookieless="UseCookies"
mode="StateServer"
stateConnectionString="tcpip=127.0.0.1:42424"
timeout="60"
/>
解决方案:
两台服务器的session都存放到同一台服务器的aspnet_state服务中
需要开启42424端口的外部访问权限
打开注册表:
HKEY_LOCAL_MacHINE/SYSTEM/CurrentControlSet/Services/aspnet_state /Parameters
节点
将AllowRemoteConnection
的值改为1
(1 为允许远程电脑的连接,0 代表禁止)
也可以修改aspnet_state服务的端口号Port
,默认42424
测试方案
在浏览器通过两个ip A B访问两个服务器,session不能共享,因为不在同一个域下面
本地测试没有域名到ip的映射,尝试使用ngnix的反向代理,
访问同一个ip,分别轮询映射到两台服务器的地址上面,在这种情况下是否会共享session呢?