面试题-字符串的处理 近义词正反双向替换

试题

$words = array(
'我'=>'你',

'不错'=>'很好',

'cc'=>'XXX',

);

$str = '你和我今天很好,XXX,是不是不错';

替换后结果 我和你今天不错,cc,是不是很好

(近义词可能是任意词语,给定的数组的键替换成值,值替换成键)

解决方案

参考代码:

<?php

$words = array(
    '我'=>'你',
    '不错'=>'很好',
    'cc'=>'XXX',
);
$str = '你和我今天很好,XXX,是不是不错';

$words = array_merge($words, array_flip($words));
$regex = "/" . implode('|', array_map(function($word) {
    $word = "(" . str_replace(['"'], [''], json_encode($word)) . ")";
    $word = preg_replace("/\\\u(.{4})/", "\x{\$1}", $word);
    return $word;
}, array_keys($words))) . "/u";

echo preg_replace_callback($regex, function($matches) use ($words) {
    $keyword = end($matches);
    return $words[$keyword];
}, $str);
2017/8/9 posted in  面试题 PHP

面试题-在页面上显示一张图片

项目需求

在页面上显示一张图片,此图片是由多个部分组成:背景,小岛和海鸥。根据传入参数的不同,小岛和海鸥在背景中的位置将可以变化。

<?php

class pic
{
    private $_url;

    public function __get($name)
    {
        return $this->$name;
    }

    public function __set($name,$value)
    {
        return $this->$name = $value;
    }
}
interface layer
{
    public function loadPic(pic $pic);
}

class background implements layer
{
    private $_z = 0;
    private $_pic;
    private $_width;
    private $_height;
    private $_html;

    public function loadPic(pic $pic)
    {
        $this->_pic = $pic;
    }

    public function __get($name)
    {
        return $this->$name;
    }

    public function __set($name,$value)
    {
        return $this->$name = $value;
    }

    public function generateHtml()
    {
        $this->_html = "<img src='{$this->_pic->_url}' width='{$this->_width}' height='{$this->_height}' style='z-index: {$this->_z};'>";
        return $this->_html;
    }
}

abstract class onLayer implements layer
{
    private $_x;
    private $_y;
    private $_z;
    private $_pic;
    private $_width;
    private $_height;
    private $_html;
    private $_downLayer;

    public function loadPic(pic $pic)
    {
        $this->_pic = $pic;
    }

    public function __get($name)
    {
        return $this->$name;
    }

    public function __set($name,$value)
    {
        return $this->$name = $value;
    }

    abstract public function on(layer $newLayer);

    public function generateHtml()
    {
        $this->_html = $this->_downLayer->_html."<img src='{$this->_pic->_url}' width='{$this->_width}' height='{$this->_height}' style='z-index: {$this->_z}; position: absolute; left: {$this->_x}px; top: {$this->_y}px;'>";
        return $this->_html;
    }
}

class island extends onLayer
{
    public function on(layer $downLayer)
    {
        $this->_width = 100;
        $this->_height = 50;
        $this->_downLayer = $downLayer;
        $this->_z = $downLayer->_z + 20;
    }
}

class bird extends onLayer
{
    public function on(layer $downLayer)
    {
        $this->_x = 20;
        $this->_y = 30;
        $this->_width = 20;
        $this->_height = 20;
        $this->_downLayer = $downLayer;
        $this->_z = $downLayer->_z + 21;
    }
}

$inland_x = isset($_REQUEST['_x']) ? $_REQUEST['_x'] : 0;
$inland_y = isset($_REQUEST['_y']) ? $_REQUEST['_y'] : 0;

No.1

开始写代码,显示由背景、小岛和海鸥三个图片组合而成的背景图

/*
背景图片为: sea.jpg
岛的图片为: island.jpg
鸟的图片为: bird.jpg
*/
$pic = new pic();
$pic->_url = 'sea.jpg';
$background = new background();
$background->loadPic($pic);
$background->_width = '400';
$background->_height = '300';
$background->generateHtml();


$pic1 = new pic();
$pic1->_url = 'island.jpg';
$island = new island();
$island->loadPic($pic1);
$island->_x = $inland_x;
$island->_y = $inland_y;
$island->on($background);
$island->generateHtml();

$pic2 = new pic();
$pic2->_url = 'bird.jpg';
$bird = new bird();
$bird->loadPic($pic2);
$bird->on($island);
$bird->_x = $inland_x;
$bird->_y = $inland_y;


$seaPic = $bird->generateHtml();
echo $seaPic;
//end_code

?>

<form action="#" method="post" style="">
    <input name="_x" type="text">
    <input name="_y" type="text">
    <input type="submit">
</form>

三张素材图

2017/3/15 posted in  面试题 PHP

面试题-使用IE的ActiveX对象过滤敏感词

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <script type="text/javascript">
        // -------------- 全局变量,用来判断文本域中是否包含脏词,默认为false,即不包含脏词-------
        var isDirty = false;
     //读取本地dirtyword词库   
    function readFile(filename){
    /*函数编辑区*/
        try {
            var fso = new ActiveXObject("Scripting.FileSystemObject");
            var file = fso.OpenTextFile(filename, 1);
            var fileContent = file.ReadAll();
            file.Close();
            return fileContent.split(' ');
        } catch (e) {
            alert('请开启ActiveX'); 
        }
    }

    

        /*
        * 提交表单的主方法
        * 在提交表单的时候对内容进行过滤并在文本域显示过滤后的内容
        */
        function submitForm1() {
            /*函数编辑区*/
            var messageValue = document.getElementById('message').value;
            messageValue = filterWord(messageValue);
            if (isDirty) {
            alert(messageValue);
                document.getElementById('message').value = messageValue;
                if (confirm('你的留言中含有不恰当的词语,系统已自动为你修改,是否继续提交?')) {
                    return true;
                }
                return false;
            }
            return true;
        }
        /*
        * 对传进来的messageValue过滤并返回新内容
        
        */
        function filterWord(messageValue) {
            // 根据文本域的id获取文本域对象内容
            /*函数编辑区*/
            var dirtyWords = readFile('c:/ciku.txt');
            for (var i = 0; i < dirtyWords.length; i++) {
                isDirty = true;
                messageValue = filterOneWord(messageValue, dirtyWords[i]);
            }
            return messageValue;
        }
        /*
        * 这个函数用来过滤单个词语, 如果messageValue中含有oneDirtyWord, 则用"**"替换这个oneDirtyWord
        * messageValue --- 要过滤的语句

        */
        function filterOneWord(messageValue, oneDirtyWord) {
        /*函数编辑区*/
        var regexp = new RegExp(oneDirtyWord,  'gi');
        return messageValue.replace(regexp, '**');
        }
 </script>
</head>
<body>
    <form name="message_board" id="message_board" action="aaa.html">
        <textarea name="message" id="message" cols="50" rows="10">
"In a world that's changing really quickly, the biggest risk is not taking any risk."
—— Mark Zuckerberg
        </textarea><br/>
        <input type="button" value="提交留言" id="submitMessage" onclick="submitForm1()"/>
    </form>
</body>
</html>
2017/2/21 posted in  面试题 PHP

数据api接口的要点

因为最近面试反馈被问到了接口,所以在此总结接口涉及到的内容。

在工作中、接口涉及到编写app接口、因为编写app接口涉及到Java(安卓app)、Object C(IOSapp)与php后端的数据交换、所以编写app接口的核心数据交换格式是JSON。

编写app接口还有一点难点是保持已登录的用户的状态、因为app程序和浏览器不同的一点是、app默认是不支持cookie、所以默认app无法保持登录状态、所以常见的有以下三种解决方式

app在发送请求时、每次请求都模仿浏览器携带cookie,在cookie中包含session_id
app端登录成功后、php服务器端为登录的用户生成一个唯一的随机字符串、并返回给app、app在后面的请求里都携带上这个随机字符串、服务器通过这个随机字符串寻找对应的用户。
其它的基于唯一字符串的验证方式:比如JWT 
https://jwt.io/
除了app接口、还有不需要用户和用户状态保持的接口、比如支付宝的支付、没有状态的保持、只需要保证和接口通信的安全性。支付宝采用的是非对称加密和使用https协议进行数据传输、避免中间人攻击。

app接口设计要点
app接口设计包含以下4点

接口的作用
接口的地址
接口的参数、参数通常是post请求携带payload方式传递的json字符串、需要通过file_get_contents('php://input')进行接收、当然也可以模拟普通的html表单进行提交、在php中使用$_POST接收。
接口的返回值、通常会返回一个状态码表示接口是否成功执行
{
code:0,
message: "success",
data: { key1: value1, key2: value2, ... }
}
 其中的code表示状态码、状态码举例、其中为0的状态码表示成功、其它均表示错误:

0:成功
100:请求错误
101:缺少appKey
102:缺少签名
103:缺少参数
200:服务器出错
201:服务不可用
202:服务器正在重启
其中的message表示消息、成功、表示成功的消息、失败、返回失败的消息、这个消息一般用于直接显示给用户。

还有的接口简化了状态码的设计、只返回成功或者失败的状态、所以、状态由true和false表示,true表示接口执行成功、false表示错误。

还有的直接使用http状态码作为接口的状态码返回给客户端。

接口的安全性
我们的接口暴露在互联网上、所以存在有人恶意使用程序进行调用、程序可以以极快的速度请求我们的接口、造成我们的接口无法服务其它正常用户、所以针对这个问题、可以对一个ip的调用次数在单位时间内进行限制、比如一个每分钟只允许60次请求、超过60次即认为非法请求、后面的请求拒绝提供服务。接口请求的次数可以保存在redis、memcache等高速缓存中、从而不影响接口的性能。

同时为了禁止非授权用户调用我们的接口、可以要求需要调用接口的第三方在我们的平台上进行注册、注册成功后、我们分配给一个固定的唯一的随机字符串、要求用户请求时必须携带此参数、并在我们的接口里进行校验、不匹配直接不提供服务。比如有道接口,百度地图接口

Restful接口
restful接口的要点就是利用请求动词(get,post)表示出接口调用方的意图(获取、删除、还是保存数据)

GET(SELECT):从服务器取出资源(一项或多项)。
POST(CREATE):在服务器新建一个资源。(客户端提供资源的信息)
PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。
PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)。
DELETE(DELETE):从服务器删除资源。
HEAD:获取资源的元数据。
OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的。
其中最常用的是前面5个请求动词、分别对应增删改查。其余用法和普通api没有区别。

接口调试工具
chrome扩展postman:https://chrome.google.com/webstore/detail/postman/fhbjgbiflinjbdggehcddcbncdddomop?hl=zh-CN

抓包工具fiddler:http://www.telerik.com/fiddler

php请求第三方接口
可以使用file_get_contents()默认以get方式请求、但是在实际开发中、为了增强对请求过程的控制、一般使用curl进行发起请求。

使用curl发起post请求

function curl_post(\(data, \)url, \(headers = array())
{
    \)ch = curl_init();
    \(res= curl_setopt (\)ch, CURLOPT_URL, \(url);
    curl_setopt(\)ch, CURLOPT_SSL_VERIFYHOST, FALSE);
    curl_setopt(\(ch, CURLOPT_SSL_VERIFYPEER, FALSE);
    curl_setopt(\)ch, CURLOPT_HEADER, 0);
    curl_setopt(\(ch, CURLOPT_POST, 1);
    curl_setopt(\)ch, CURLOPT_POSTFIELDS, \(data);
    curl_setopt(\)ch, CURLOPT_RETURNTRANSFER, 1);
    \(result = curl_exec(\)ch);
    curl_close(\(ch);
    if (\)result == NULL) {
        return 0;
    }
    return $result;
}
使用curl发起get请求

function curl_get(\(data, \)url, \(headers = array())
{
    \)ch = curl_init();
    \(res= curl_setopt (\)ch, CURLOPT_URL, \(url);
    curl_setopt(\)ch, CURLOPT_SSL_VERIFYHOST, FALSE);
    curl_setopt(\(ch, CURLOPT_SSL_VERIFYPEER, FALSE);
    curl_setopt(\)ch, CURLOPT_HEADER, 0);
    curl_setopt(\(ch, CURLOPT_POSTFIELDS, \)data);
    curl_setopt(\(ch, CURLOPT_RETURNTRANSFER, 1);
    \)result = curl_exec(\(ch);
    curl_close(\)ch);
    if (\(result == NULL) {
        return 0;
    }
    return \)result;

 第三方接口一般返回json字符串、我们对json字符串使用json_decode进行处理成php的对象或数组、就可以使用php操作变量的方式对返回的字符串进行处理。

友盟数据接口的使用方式

授权接口

\(url = 'http://api.umeng.com/authorize' ;
\)data = 'email=你的邮箱&password=你的密码';
echo curl_post(\(data, \)url);

获取app基本数据的接口

\(url = "http://api.umeng.com/apps/base_data";
\)data = '';
\(headers = array(
    'Authorization: Basic 你的auth_token'
);
echo curl_get(\)data, \(url, \)headers);

参考资料

Keegan小钢

2017/2/21 posted in  面试题

面试题-求多叉树两节点的最短路径

第一种解法

function findParentNode($nodes, $id)
{
    foreach ($nodes as $node) {
        if (in_array($id, $node['childrens'])) {
            return $node;
        }
    }
    return false;
}

function minimum2($nodes, $from, $to)
{
    $fromToTop = array($from);
    while ($parentNode = findParentNode($nodes, $from)) {
        $fromToTop[] = $parentNode['id'];
        $from = $parentNode['id'];
    }
    $toToTop = array($to);
    while ($parentNode = findParentNode($nodes, $to)) {
        $fromToTop[] = $parentNode['id'];
        $to = $parentNode['id'];
    }
    $path = false;
    $fromPath = array();
    $toPath = array();
    $countf = count($fromToTop);
    $countt = count($toToTop);
    for ($i=0; $i < $countf; $i++) {
        $fromPath[] = $fromToTop[$i];
        for ($j=0; $j < $countt; $j++) { 
            $toPath[] = $toToTop[$j];
            if ($fromToTop[$i] == $toToTop[$j]) {
                // 共同点。
                // 去掉一个共同点
                array_pop($fromPath);
                $path = implode('=>', $fromPath) . '=>' . implode('=>', $toPath);
                break 2;
            }
        }
        $toPath = array();
    }
    return $path;
}

第二种解法

function findParentNode($nodes, $id)
{
    foreach ($nodes as $node) {
        if (in_array($id, $node['childrens'])) {
            return $node;
        }
    }
    return false;
}

function minimum($nodes, $from, $to, $path = '', $tryParent = true)
{
    static $finds = array();
    static $excludeIds = array();
    if ($from == $to) {
        $path = $path . $from;
        $finds[] = $path;
        return $finds;
    }
    $path = $path . $from . '=>';

    $startNode = $nodes[$from];
    // 在子节点中找
    foreach ($startNode['childrens'] as $childId) {
        if (in_array($childId, $excludeIds)) {
            continue;
        }
        $childNode = $nodes[$childId];
        minimum($nodes, $childNode['id'], $to, $path, false);
    }

    $parentNode = findParentNode($nodes, $startNode['id']);
    $excludeIds[] = $startNode['id'];
    if ($tryParent && $parentNode) {
        minimum($nodes, $parentNode['id'], $to, $path, true);
    }

    usort($finds, function($a, $b) {
        $strLenA = strlen($a);
        $strLenB = strlen($b);
        if ($strLenA == $strLenB) {
            return 0;
        }
        return ($strLenA < $strLenB) ? -1 : 1;
    });
    return isset($finds[0]) ? $finds[0] : false;
}

测试

$nodes=array(
    array(
        'id' => 'A',
        'childrens' => array('B', 'C', 'D'),
    ),
    array(
        'id' => 'B',
        'childrens' => array('E', 'F', 'G'),
    ),
    array(
        'id' => 'C',
        'childrens' => array(),
    ),
    array(
        'id' => 'D',
        'childrens' => array('H', 'I', 'J'),
    ),
    array(
        'id' => 'E',
        'childrens' => array(),
    ),
    array(
        'id' => 'F',
        'childrens' => array('K', 'L', 'N'),
    ),
    array(
        'id' => 'G',
        'childrens' => array(),
    ),
    array(
        'id' => 'H',
        'childrens' => array('O', 'P', 'Q'),
    ),
    array(
        'id' => 'I',
        'childrens' => array(),
    ),
    array(
        'id' => 'J',
        'childrens' => array(),
    ),
    array(
        'id' => 'K',
        'childrens' => array(),
    ),
    array(
        'id' => 'L',
        'childrens' => array(),
    ),
    array(
        'id' => 'N',
        'childrens' => array('R', 'S', 'T'),
    ),
    array(
        'id' => 'O',
        'childrens' => array(),
    ),
    array(
        'id' => 'P',
        'childrens' => array(),
    ),
    array(
        'id' => 'Q',
        'childrens' => array(),
    ),
    array(
        'id' => 'R',
        'childrens' => array(),
    ),
    array(
        'id' => 'S',
        'childrens' => array(),
    ),
    array(
        'id' => 'T',
        'childrens' => array(),
    ),
);
$idNodeMap = array();
foreach($nodes as $key => $value) {
    $idNodeMap[$value['id']] = $value;
}

var_dump(minimum($idNodeMap, 'T', 'A'));
var_dump(minimum2($idNodeMap, 'T', 'A'));
2017/2/14 posted in  面试题 PHP